Sping AOP
Spring AOP:控制反转
AOP面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
代理模式
代理模式
是程序设计中的一种设计模式
- 所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源
代理模式分类:
- 静态代理
- 动态代理
优势:
- 真实角色更加纯粹,不用关注公共业务
- 公共业务交给代理角色,实现业务分工
- 公共业务发生扩展时,方便集中管理
劣势:
- 一个真实角色会产生一个代理,代码量会翻倍、效率降低
静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实代理:被代理的角色
- 代理角色:代理真实角色
- 客户:访问代理对象
- 接口
public interface Rent { public void rent(); }
- 代理角色
public class Proxy implements Rent{ private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } public void rent(){ host.rent(); } //看房 public void seeHouse(){ System.out.println("中介带你看房"); } //收中介费 public void money(){ System.out.println("收中介费"); } }
- 客户端访问代理角色
public class Client { public static void main(String[] args) { //创建房东对象 Host host = new Host(); //代理 Proxy proxy = new Proxy(host); //通过代理调用房东对象 proxy.rent(); } }
动态代理
动态代理:
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成
- 动态代理是一个接口,对应一类业务
- 动态代理可以代理多个类,只要实现一个接口
动态代理分类:
- 基于接口——JDK动态代理
- 基于类——cglib
- Java字节码——Javasist
两种类了解:
- Proxy——代理
- InvocationHandl——调用处理程序
Proxy类:生成动态代理实例
Proxy
创建动态代理类的实例提供了静态方法,也是所有动态代理类的父类的方法创建方法:
//返回指定的代理实例的调用处理程序 getInvocationHandler(Object proxy) //返回 java.lang.Class对象的代理类,给出了类装载器和一个阵列的接口 getProxyClass(ClassLoader loader, 类<?>... interfaces) //如果指定的类是动态生成的可利用 getProxyClass法或代理类 newProxyInstance方法返回true isProxyClass(类<?> cl) //返回指定的接口,将方法调用指定的调用处理程序的代理类的一个实例。 newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
InvocationHandl类:调用处理程序并返回结果
InvocationHandler
是通过一个代理实例调用处理程序实现的接口。- 每个代理实例都有一个相关的调用处理程序。当一个方法是在一个代理实例调用,调用的方法进行编码并派遣其调用处理程序的
invoke
方法方法:
//在代理实例上处理方法调用,并返回结果 invoke(Object proxy, 方法 method, Object[] args)
- proxy — 调用该方法的代理实例
- method — 反射获取到的接口的方法
- args — 使用此方法时传入的参数数组
- ProxyInvocationHandler动态代理类
public class ProxyInvocationHandl implements InvocationHandler { //被代理的接口 private Object target; public void setTarget(Object target) { this.target = target; } //生成得到代理类 public Object getProxy() { //this.getClass().getClassLoader() 加载类所在位置 //rent.getClass().getInterfaces() 表示代理接口 //this == InvocationHandler实现类 return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } //处理代理实例,并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质,就是使用反射机制 log(method.getName()); Object result = method.invoke(target, args); return result; } public void log(String msg) { System.out.println("执行了" + msg + "方法"); } }
- Client客户类
public class Client { public static void main(String[] args) { //真实角色:被代理的角色 UserServiceImp userService = new UserServiceImp(); //代理角色:代理真实角色 ProxyInvocationHandl pih = new ProxyInvocationHandl(); //设置代理对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService) pih.getProxy(); proxy.query(); } } /* 执行了query方法 [Debug]使用了query方法 查询用户 */
AOP概述
提供声明式事物,允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是很切关注点,如:日志,安全,缓存,事物……
- 切面(ASPECT):横切关注点被模块化的特殊对象。
- 通知(Advice):切面必须要完成的工作。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知执行的"地点"的定义。
- 连接点(JoinPoint):与切入点匹配的执行点。
Spring实现AOP
方式一:使用Spring的API接口实现AOP
- 使用AOP织入,需要导入一个依赖包
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies>
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> </beans>
- 前置Log日志类
public class log implements MethodBeforeAdvice { //method:要执行的目标对象方法 //object:参数 //target:目标对象 @Override public void before(Method method, Object[] objects, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了"); } }
- 后置AfterLog类
public class AfterLog implements AfterReturningAdvice { //returnValue:返回值 @Override public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了"+method.getName()+"放回结果为:"+returnValue); } }
- UserService接口
public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
- UserServiceImpl接口实现类
public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete() { System.out.println("删除了一个用户"); } public void update() { System.out.println("更新了一个用户"); } public void select() { System.out.println("查询了一个用户"); } }
- 将UserServiceImpl接口实现类注册到Spring中,即applicationContext.xml中
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--注册bean--> <bean id="userService" class="com.wei.service.UserServiceImpl"/> <bean id="log" class="com.wei.log.log"/> <bean id="agter" class="com.wei.log.AfterLog"/> <!--方式一:使用原生的Spring API接口--> <!--配置aop,需要导入aop的约束--> <aop:config> <!--切入点: execution:表达式 execution(要执行的位置 * * * * *) --> <aop:pointcut id="pointcut" expression="execution(* com.wei.service.UserServiceImpl.*(..))"/> <!--执行环绕增强--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="agter" pointcut-ref="pointcut"/> </aop:config> </beans>
- MyTest测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理的是接口 UserService userService = (UserService) context.getBean("userService"); userService.delete(); } } /* 执行结果: com.wei.service.UserServiceImpl的delete被执行了 删除了一个用户 执行了delete放回结果为:null */
方式二:自定义实现AOP
- 自定义Diy类
public class DiyPointCut { public void before(){ System.out.println("===========方法执行前==========="); } public void after(){ System.out.println("===========方法执行后==========="); } }
- 将Diy类注册到Spring中,即applicationContext.xml中
<!--方式二:自定义类--> <bean id="diy" class="com.wei.diy.DiyPointCut"/> <aop:config> <!--自定义切面:ref要引用的类--> <aop:aspect ref="diy"> <!--切入点--> <!--execution(修饰符返回值 类路径.*(..))--> <aop:pointcut id="point" expression="execution(* com.wei.service.UserServiceImpl.*(..))"/> <!--通知--> <!-- method="before" 自定义的方法名 pointcut-ref="point" 方法执行在point切入点之后 --> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>
- MyTest测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理的是接口 UserService userService = (UserService) context.getBean("userService"); userService.delete(); } } /* ===========方法执行前=========== 删除了一个用户 ===========方法执行后=========== */
aop中execution 表达式(*execution(\* 包名.\*.*(..))*)
- 整个表达式可以分为五个部分:
execution(): 表达式主体
第一个*号:方法返回类型, *号表示所有的类型
包名:表示需要拦截的包名
第二个*号:表示类名,*号表示所有的类
*(…):最后这个星号表示方法名,*号表示所有的方法,后面( )里面表示方法的参数,两个句点表示任何参数
<!--切入点--> <!--execution(修饰符返回值 类路径.*(..))--> <aop:pointcut id="point" expression="execution(* com.wei.service.UserServiceImpl.*(..))"/>
方式三:使用注解实现AOP
- 创建AnnotationPointCut类
//方式三:使用注解实现AOP // @Aspect 标注这个类是一个切面 @Aspect public class AnnotationPointCut { //后置增强 @Before("execution(* com.wei.service.UserServiceImpl.*(..))") public void before() { System.out.println("======方法执行前======"); } //前置增强 @After("execution(* com.wei.service.UserServiceImpl.*(..))") public void after() { System.out.println("======方法执行后======"); } //在环绕增强中,可以给一个参数,代表要获取处理切入的点 @Around("execution(* com.wei.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("======环绕前======"); //执行方法 Object proceed = jp.proceed(); System.out.println("======环绕后======"); } }
- 将AnnotationPointCut类注册到Spring中,即applicationContext.xml中
<!--方式三--> <bean id="AnnotationPointCut" class="com.wei.diy.AnnotationPointCut"/> <!--开启注解支持--> <!-- JDK(默认 proxy-target-class="false") cglib(proxy-target-class="true") --> <aop:aspectj-autoproxy proxy-target-class="true"/>
- MyTest测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理的是接口 UserService userService = (UserService) context.getBean("userService"); userService.delete(); } } /* 执行结果: ======环绕前====== ======方法执行前====== 删除了一个用户 ======环绕后====== ======方法执行后====== */