目录
为什么要说动态代理:
/*
* 静态代理类
* 实现接口UserManager
* 引入UserManagerImpl
* */
public class UserManagerImplProxy implements UserManager {
private UserManagerImpl userManagerImpl;
// 构造方法
public UserManagerImplProxy(UserManagerImpl userManagerImpl) {
this.userManagerImpl = userManagerImpl;
}
// 具体方法
public void addUser(String username, String password) {
// 执行检查方法
checkSecurity();
// 调用具体类的实现方法
userManagerImpl.addUser(username, password);
}
public void delUser(int userId) {
checkSecurity();
userManagerImpl.delUser(userId);
}
public String findUserById(int userId) {
checkSecurity();
return userManagerImpl.findUserById(userId);
}
public void modifyUser(int userId, String username, String password) {
checkSecurity();
userManagerImpl.modifyUser(userId, username, password);
}
// 检查的方法
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
这是典型的静态代理,也就是在运行之前代码编辑好的,静态代理避免了修改原有的类,为了实现基本的开闭原则,在不修改原代码的基础上重新写一个代理类,来实现需要添加的功能。
但是同时这也是它的局限,它并没有避免多次调用checkSecurity()这个方法,每一个需要被代理的类都要有一个代理类,每一个代理类下的每一个方法前面都需要添加这样的方法,在代码量很大的情况下,就会很冗余。
所以出现了动态代理,不需要提前写好代理类,在程序运行的时候加载创建需要的代理类。
这也就是为什么要用动态代理的原因。
动态代理的实现:
public class SecurityHandler implements InvocationHandler {
private Object targetObject;
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("-------checkSecurity-------");
//调用目标方法
Object ret = method.invoke(targetObject, args);
return ret;
}
}
这里method.invoke(目标类,目标类参数);只需要把需要被代理的类传给这个SecurityHandler类,它就可以动态创建代理类并执行invoke方法在执行方法之前添加上需要的内容
PS:动态代理,相当于在火腿肠出厂之前,把所有这一批生成的火腿肠,都打上生产日期的码,返回的东西,都是打码后的火腿肠,你在吧火腿肠放入打码器之前,需要先告诉它这个是什么食品:火腿肠。
也就是在需要告诉动态代理,这个需要被代理的类是谁,之后动态代理会执行相应的加工方法invoke(),对传入的类进行处理。
执行结果:
动态代理的分类:
-
JDK动态代理,是有接口的类的代理
-
CGlib动态代理,是没有接口的
Spring 的AOP
<!-- 强制使用CGLIB代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
spring AOP管理的两种风格:实现方式不同
一、 使用AspectJ
通过在spring的配置文件中配置<!-- 启用AspectJ对Annotation的支持 --> <aop:aspectj-autoproxy/>
把刚刚动态代理的部分,用这样注解的方式代替标识。
@Aspect
public class SecurityHandler {
/**
* 定义Pointcut,Pointcut的名称为addAddMethod(),此方法没有返回值和参数
* 该方法就是一个标识,不进行调用
*/
@Pointcut("execution(* add*(..))")
private void addAddMethod(){};
/**
* 定义Advice,表示我们的Advice应用到哪些Pointcut订阅的Joinpoint上
*/
@Before("addAddMethod()")
//@After("addAddMethod()")
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
这里涉及到一些AOP的基础概念:
* 切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
* 连接点(Joinpoint): 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。
* 通知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。
* 切入点(Pointcut): 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
* 引入(Introduction): (也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。 例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
* 目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
* AOP代理(AOP Proxy): AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。
* 织入(Weaving): 把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
二、使用xml配置文件
可以直接省略掉标识@Aspect,直接使用配置文件配置需要切入的类,切入的方法位置,以及具体执行的内容都可以在这里配置
<aop:config>
<aop:aspect id="securityAspect" ref="securityHandler">
<!--
以add开头的方法
<aop:pointcut id="addAddMethod" expression="execution(* add*(..))"/>
-->
<!--
com.bjpowernode.spring包下所有的类所有的方法
<aop:pointcut id="addAddMethod" expression="execution(* com.bjpowernode.spring.*.*(..))"/>
-->
<aop:pointcut id="addAddMethod" expression="execution(* com.bjpowernode.spring.*.add*(..)) || execution(* com.bjpowernode.spring.*.del*(..))"/>
<aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>
</aop:aspect>
</aop:config>
// 这是具体的执行类
public class SecurityHandler {
private void checkSecurity(JoinPoint joinPoint) {
for (int i=0; i<joinPoint.getArgs().length; i++) {
System.out.println(joinPoint.getArgs()[i]+"-----这是joinPoint.getArgs()的第"+ i);
}
System.out.println(joinPoint.getSignature().getName()+"------这是joinPoint.getSignature().getName()");
System.out.println("-------checkSecurity-------");
}
}
运行结果: