Spring AOP原理之动态代理
什么是代理?
指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上, 增强目标对象的业务逻辑.
静态代理
静态代理的特点是, 为每一个业务增强都提供一个代理类, 由代理类来创建代理对象. 下面我们通过静态代理来实现对转账业务进行身份验证.
(1) 转账业务
public interface IAccountService {
//主业务逻辑: 转账
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("调用dao层,完成转账主业务.");
}
}
(2) 代理类
public class AccountProxy implements IAccountService {
//目标对象
private IAccountService target;
public AccountProxy(IAccountService target) {
this.target = target;
}
/**
* 代理方法,实现对目标方法的功能增强
*/
@Override
public void transfer() {
before();
target.transfer();
}
/**
* 前置增强
*/
private void before() {
System.out.println("对转账人身份进行验证.");
}
}
(3) 测试
public class Client {
public static void main(String[] args) {
//创建目标对象
IAccountService target = new AccountServiceImpl();
//创建代理对象
AccountProxy proxy = new AccountProxy(target);
proxy.transfer();
}
}
结果:
对转账人身份进行验证.
调用dao层,完成转账主业务.
动态代理
静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象, 而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成.
JDK动态代理
JDK动态代理是使用 java.lang.reflect 包下的代理类来实现. JDK动态代理动态代理必须要有接口.
(1) 转账业务
public interface IAccountService {
//主业务逻辑: 转账
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("调用dao层,完成转账主业务.");
}
}
(2) 增强
因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.
public class AccountAdvice implements InvocationHandler {
//目标对象
private IAccountService target;
public AccountAdvice(IAccountService target) {
this.target = target;
}
/**
* 代理方法, 每次调用目标方法时都会进到这里
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
return method.invoke(target, args);
}
/**
* 前置增强
*/
private void before() {
System.out.println("对转账人身份进行验证.");
}
}
(3) 测试
public class Client {
public static void main(String[] args) {
//创建目标对象
IAccountService target = new AccountServiceImpl();
//创建代理对象
IAccountService proxy = (IAccountService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new AccountAdvice(target)
);
proxy.transfer();
}
}
结果:
对转账人身份进行验证.
调用dao层,完成转账主业务.
CGLIB动态代理
JDK动态代理必须要有接口, 但如果要代理一个没有接口的类该怎么办呢? 这时我们可以使用CGLIB动态代理. CGLIB动态代理的原理是生成目标类的子类, 这个子类对象就是代理对象, 代理对象是被增强过的.
注意: 不管有没有接口都可以使用CGLIB动态代理, 而不是只有在无接口的情况下才能使用.
(1) 转账业务
public class AccountService {
public void transfer() {
System.out.println("调用dao层,完成转账主业务.");
}
}
(2) 增强
因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.
public class AccountAdvice implements MethodInterceptor {
/**
* 代理方法, 每次调用目标方法时都会进到这里
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
before();
return methodProxy.invokeSuper(obj, args);
// return method.invoke(obj, args); 这种也行
}
/**
* 前置增强
*/
private void before() {
System.out.println("对转账人身份进行验证.");
}
}
(3) 测试
public class Client {
public static void main(String[] args) {
//创建目标对象
AccountService target = new AccountService();
//
//创建代理对象
AccountService proxy = (AccountService) Enhancer.create(target.getClass(),
new AccountAdvice());
proxy.transfer();
}
}
结果:
对转账人身份进行验证.
调用dao层,完成转账主业务.
模拟Spring AOP场景
了解了动态代理后, 我们就可以自己来实现Spring AOP功能了, 所以下面我们来模拟下Spring AOP场景.
(1) 转账业务
public class Client {
public static void main(String[] args) {
//创建目标对象
AccountService target = new AccountService();
//
//创建代理对象
AccountService proxy = (AccountService) Enhancer.create(target.getClass(),
new AccountAdvice());
proxy.transfer();
}
}
结果:
对转账人身份进行验证.
调用dao层,完成转账主业务.
(2) 切面抽象类
定义一个切面抽象类, 该类使用了模板方法的设计模式, 为开始, 结束, 异常, 前置增强, 后置增强提供了默认实现, 当我们定义切面类时只需要按需重写它们就行. isIntercept() 方法用来判断切入点是否正确, 切面类需要重写这个方法.
public abstract class BaseAspect implements MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(BaseAspect.class);
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
begin();
try {
if (isIntercept(method, args)) {
before();
result = methodProxy.invokeSuper(obj, args);
after();
} else {
result = methodProxy.invokeSuper(obj,args);
}
} catch (Exception e) {
logger.error("proxy failure", e);
error(e);
throw e;
} finally {
end();
}
return result;
}
/**
* 开始增强
*/
public void begin() {
}
/**
* 切入点判断
*/
public boolean isIntercept(Method method, Object[] args) throws Throwable {
return true;
}
/**
* 前置增强
*/
public void before() throws Throwable {
}
/**
* 后置增强
*/
public void after() throws Throwable {
}
/**
* 异常增强
*/
public void error(Throwable e) {
}
/**
* 最终增强
*/
public void end() {
}
}
(3) 切面类
创建一个切面类, 类中配置切入点和增强.
public class AccountAspect extends BaseAspect {
/**
* 切入点
*/
public boolean isIntercept(Method method, Object[] args) throws Throwable {
return method.getName().equals("transfer");
}
/**
* 前置增强
*/
public void before() throws Throwable {
System.out.println("对转账人身份进行验证.");
}
}
(4) 代理工厂类
定义一个工厂类来创建代理, 其实不创建这个类也行, 但为了模仿Spring还是创建了. @SuppressWarnings是为了抑制警告, 就是编译器上面的黄线.
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static T createProxy(final Class> targetClass, final MethodInterceptor methodInterceptor) {
return (T) Enhancer.create(targetClass,methodInterceptor);
}
}
(5) 测试
public class Client {
public static void main(String[] args) {
//创建目标对象
IAccountService target = new AccountServiceImpl();
//切面
BaseAspect accountAspect = new AccountAspect();
//创建代理对象
IAccountService proxy = (IAccountService) ProxyFactory.createProxy(target.getClass(), accountAspect);
proxy.transfer();
}
}
结果:
对转账人身份进行验证.
调用dao层,完成转账主业务.
原文链接:csdn.net/litianxiang_kaola/article/details/85335700