AOP实现原理--静态代理与动态代理
1. AOP原理
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
使用场景
日志记录
事物处理,持久化,系统统一的认证,去哪先管理
一般AOP实现两种方式
实现方式 | 原理 | 优点 | 缺点 |
---|---|---|---|
静态代理 | 在代码的编译阶段植入Pointcut的内容 | 性能好 | 需要额外的编译 |
动态代理 | 在代码执行阶段,在内存中截获对象,动态的插入Pointcut的内容 | 不需要额外的编译 | 性能比静态织入要低 |
2. Spring 中AOP实现方式
在Spring 中,虽然引入了AspectJ的语法,但是他本质上使用的是动态代理的方式.但即使是动态代理,也分两种实现模式:
1.使用接口(动态代理)
如果被代理的对象是面向接口编程的,那么Spring直接使用实现这些接口,然后把需要插入的内容在这个接口上下文中插入。
2.使用继承(CGLIB)
如果被代理的对象没有基于接口编程,那么Spring会调用cglib库,通过子类继承的方式,动态插入需要的内容,并且调用父类的方法实现。
cglib内部拥有一个小的字节码处理框架asm,来转换字节码生成新的类.所以spring调用了cglib,相当于生成了一个被代理对象的子类,来取代被代理对象。
3. 动态代理VS静态代理
3.1 静态代理
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
静态代理通过实现与被代理类相同的接口,并且拥有被代理对象的引用,从而进行代理。
业务接口
package proxy;
/**
* Created by YuanXiang on 2017/9/2.
*/
public interface UserManager {
void addUser(String userId, String userName);
void delUser(String userId);
String findUser(String userId);
void modifyUser(String userId, String userName);
}
被代理类
package proxy;
/**
* Created by YuanXiang on 2017/9/2.
*/
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser");
}
@Override
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
代理类
package proxy;
/**
* Created by YuanXiang on 2017/9/2.
*/
public class UserManagerImplStaticProxy implements UserManager {
private UserManager userManager;
public UserManagerImplStaticProxy(UserManager userManager){
this.userManager = userManager;
}
@Override
public void addUser(String userId, String userName) {
System.out.print("start ---");
this.userManager.addUser(userId, userName);
System.out.print("end --");
}
@Override
public void delUser(String userId) {
}
@Override
public String findUser(String userId) {
return null;
}
@Override
public void modifyUser(String userId, String userName) {
}
}
调用
package proxy;
/**
* Created by YuanXiang on 2017/9/2.
*/
public class Client {
public static void main(String[] args) {
UserManager proxy = new UserManagerImplStaticProxy(new UserManagerImpl());
proxy.addUser("312","123");
}
}
3.2 动态代理
动态代理的实现方式是依据java的反射机制。依赖于
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
原理是使用java中Proxy类的静态方法生成被代理类的代理对象,并把其放入实现InvocationHandler接口的对象中,实现InvocationHandler接口的类会要要求重写invoke方法,从而在调用代理对象方法时会自动调用invoke方法。
日志代理类
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by YuanXiang on 2017/9/2.
*/
public class LogHandler implements InvocationHandler{
private Object targetObject;
public Object newProxyInstance(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start-->>");
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
}
Object ret=null;
try{
System.out.println("start-->");
ret = method.invoke(targetObject, args);
System.out.println("success-->");
}catch (Exception e){
e.printStackTrace();
System.out.println("error-->");
throw e;
}
return ret;
}
}
调用类
package proxy;
/**
* Created by YuanXiang on 2017/9/2.
*/
public class Client {
public static void main(String[] args) {
LogHandler logHandler = new LogHandler();
UserManager userManager = (UserManager) logHandler.newProxyInstance(new UserManagerImpl());
userManager.addUser("1111", "张三");
}
}
3.3 总结
看完静态代理以及动态代理可以发现,静态代理一般一个代理类只能代理一个类,而动态代理则不同,上述的日志动态代理类可以把代理任何类,把日志输出到任何需要的地方。但是两者都是在面向接口编程的基础之上。
参考文章