AOP
一、静态代理
静态代理就是有一个公共的接口,接口中有一些业务方法,称为抽象角色。一个类实现这个接口,专注于里面业务方法的实现而不去关注其他的东西,称为真实角色。再写一个类实现这个接口,通过组合的方式,将真实角色包含其中,可以另外增加一些其他的方法,扩展附属业务,称为代理角色。
角色分析:
●抽象角色:一般会使用接口或者抽象类来解决
●真实角色:被代理的角色
●代理角色:代理真实角色,代理真实角色后,我们一般会做一 些附属操作
●客户:访问代理对象的人!
代理模式的好处:
●可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
●公共也就就交给代理角色!实现了业务的分工!
●公共业务发生扩展的时候,方便集中管理!
缺点:
●一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低~
下面写一个实例,使用代理模式实现给真实对象的每个方法执行时打印日志。
抽象角色
public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
真实角色
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("查找了一个用户"); } }
代理角色
public class UserServiceProxy implements UserService{ private UserServiceImpl userService; public void setUserService(UserServiceImpl userService) { this.userService = userService; } public void add() { log("add"); userService.add(); } public void delete() { log("delete"); userService.delete(); } public void update() { log("update"); userService.update(); } public void select() { log("select"); userService.select(); } public void log(String msg){ System.out.println("使用了"+msg+"方法"); } }
实际开发中如果需要给原有业务增加一些功能,我们不可能直接修改原业务代码,这时我们就可以使用这种代理模式来实现。
二、动态代理
●动态代理和静态代理角色一样
●动态代理的代理类是动态生成的,不是我们直接写好的!
●动态代理分为两大类:基于接口的动态代理,基于类的动态代理
。基于接口-- JDK动态代理 [我们在这里使用]
。基于类: cglib
。java字节码实现: javasist
需要了解两个类: Proxy: 代理,InvocationHandler: 调用处理程序
三、AOP
1、什么是AOP
AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2、AOP在Spring中的作用
提供声明式事务;允许用户自定义切面
●横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等...
●切面(ASPECT) :横切关注点 被模块化的特殊对象。即,它是一个类。
●通知(Advice) :切面必须要完成的工作。即,它是类中的一个方法。
●目标(Target)+:被通知对象。
●代理(Proxy) :向目标对象应用通知之后创建的对象。
●切入点(PointCut) :切面通知执行的“地点”的定义。
●连接点(JointPoint) :与切入点匹配的执行点。
3、使用Spring实现AOP
使用AOP织入,需要导入依赖包
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.9</version> </dependency>
写Service层的接口和方法,使用AOP给每个方法加日志打印。
public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
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 query() { System.out.println("查找了一个用户"); } }
方式一、使用Spring的接口
写日志实现类,前置方法和后置方法
public class Log implements MethodBeforeAdvice { //method:要执行的目标对象的方法;args:参数;target:目标对象 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了"); } }
public class AfterLog implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"方法,结果是"+returnValue); } }
在xml中注册我们的bean,并实现aop,将日志中方法切入到我们的业务方法前后。
<?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.along.service.UserServiceImpl" /> <bean id="log" class="com.along.log.Log"/> <bean id="afterLog" class="com.along.log.AfterLog"/> <!--配置aop:需要导入aop的约束--> <aop:config> <!--切入点:expression:表达式,execution(要执行的位置)--> <aop:pointcut id="pointcut" expression="execution(* com.along.service.UserServiceImpl.*(..))"/> <!--执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理代理的是接口 UserService userService = context.getBean("userService", UserService.class); userService.delete(); } }
结果
方式二:自定义类来实现aop
自定义切入点类
public class DiyPointCut { public void before(){ System.out.println("=======方法执行前========="); } public void after(){ System.out.println("=======方法执行后========="); } }
<?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 id="userService" class="com.along.service.UserServiceImpl" /> <bean id="diy" class="com.along.diy.DiyPointCut"/> <aop:config> <!--自定义切面,ref要引用的类--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="pointcut" expression="execution(* com.along.service.UserServiceImpl.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="pointcut"/> <aop:after method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> </beans>
结果
方式三:使用注解实现
@Aspect //标注这个类是切面
public class AnnotationPointCut {
@Before("execution(* com.along.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("执行前");
}
@After("execution(* com.along.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("执行后");
}
@Around("execution(* com.along.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}