第一次学习Spring的时候只是觉得很好用,而且也只是停留在会用的阶段,现在在公司实习,又叫看Spring,于是就有了这篇文章,这时才体会到Spring确实优秀。(博主只是一个自学一年不到的小白,如果有错还望各位大佬批评指正)
例子:在我们实现具体的业务需求操作的时候,我们为了保证业务安全,需要在业务方法前面开启事务,方法完成之后再提交事务,如果业务出错还需要回滚事务。如果有大量的业务方法,我们就会发现业务代码变得臃肿,而且全是重复事务的操作,这时候我们就会想要重构。因此首先我们是采用装饰类来加强业务代码。(说明一下,为了直观一点,我所有的bean都用spring的xml装配,不采用@autowired自动装配)
package com.swust.service.impl;
import com.swust.dao.IEmployeeDAO;
import com.swust.dao.impl.EmployeeDAOImpl;
import com.swust.domain.Employee;
import com.swust.service.IEmployeeService;
import lombok.Setter;
public class EmployeeServiceImpl implements IEmployeeService {
@Setter
private IEmployeeDAO dao;
public void save(Employee employee) {
dao.save(employee);
}
public void update(Employee employee) {
dao.update(employee);
throw new RuntimeException("故意出错=============");
}
}
//这是包装类
package com.swust.test.wapper;
import com.swust.TranscantionManager;
import com.swust.domain.Employee;
import com.swust.service.IEmployeeService;
/**
* @author Mr
* 模仿包装类,用事务包装EmployeeServiceImpl,达到增强的目的
*/
public class EmployeeServiceWapper implements IEmployeeService{
private IEmployeeService serviceImpl;
private TranscantionManager tx;
public EmployeeServiceWapper(IEmployeeService serviceImpl, TranscantionManager tx) {
this.serviceImpl = serviceImpl;
this.tx = tx;
}
public void save(Employee e) {
try {
tx.begin();
serviceImpl.save(e);
tx.commit();
} catch (Exception e2) {
e2.printStackTrace();
tx.rollback();
}
}
public void update(Employee e) {
try {
tx.begin();
serviceImpl.update(e);
tx.commit();
} catch (Exception e2) {
e2.printStackTrace();
tx.rollback();
}
}
}
package com.swust;
//需要增强的操作,可以是事务,log等
public class TranscantionManager {
public void begin(){
System.out.println("***********开启事务***********");
}
public void commit(){
System.out.println("***********提交事务***********");
}
public void rollback(){
System.out.println("***********回滚事务***********");
}
}
我们可以看见,在包装类中需要实现真实对象的接口,需要引入真实对象并通过构造器注入真实对象,接下来看一看测试类。
package com.swust.test.wapper;
import org.junit.Test;
import com.swust.TranscantionManager;
import com.swust.domain.Employee;
import com.swust.service.impl.EmployeeServiceImpl;
public class TestWapper {
EmployeeServiceWapper wapper=new EmployeeServiceWapper(new EmployeeServiceImpl(), new TranscantionManager());
Employee e=new Employee();
@Test
public void testSave() throws Exception {
wapper.save(e);
}
@Test
public void testUpdate() throws Exception {
wapper.update(e);
}
}
可以看见这种方法确实可以增强业务方法,但是它向外暴露的真实对象,不符合封装,并且这个类不可复用,一个真实类需要一个包装类。所以改进一下引出静态代理。同样是那些业务,来看看静态代理类。
package com.swust.test.proxy.staticProxy;
import com.swust.TranscantionManager;
import com.swust.domain.Employee;
import com.swust.service.IEmployeeService;
import lombok.Setter;
/**
* @author Mr
* 模仿静态代理类,使用spring容易管理bean,静态代理没有暴露真实对象,而包装类会暴露
*/
public class EmployeeServiceStaticProxy implements IEmployeeService{
@Setter
private IEmployeeService target;
@Setter
private TranscantionManager tx;
public void save(Employee e) {
try {
tx.begin();
target.save(e);
tx.commit();
} catch (Exception e2) {
e2.printStackTrace();
tx.rollback();
}
}
}
可以看出静态代理,并没有向外暴露真实对象,因为他是无参数的构造器,在配置文件中也可以隐藏起来,其他和装饰类基本一样。
为了解决类的不可复用性,就引入了动态代理。下面来模仿一下JDK的动态代理(AOP基于动态代理)。
package com.swust.test.proxy.jdkProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.swust.TranscantionManager;
import lombok.Setter;
public class TranscantionManagerHandler implements InvocationHandler{
@Setter
private Object target;//真实对象,可以通过spring注入
@Setter
private TranscantionManager tx;
/**
* @return 代理对象
* 生成的代理类会继承Proxy类
*/
@SuppressWarnings("unchecked")
public T getProxyObj(){
Object object = Proxy.newProxyInstance(target.getClass().getClassLoader()//通过真实对象得到类加载器
, target.getClass().getInterfaces()//真实对象的接口
, this);//InvocationHandler h:这里表示当前类对象,即代理辅助类对象,最终会调用加强后的invoke方法
return (T) object;
}
/**
* 在真实对象上增强
*
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("辅助类中的method是:"+method+"===args:"+args);
System.out.println("辅助类中的args是:"+args);
System.out.println("========前面做======增强");
Object object = method.invoke(target, args);
System.out.println("========后面做======增强");
return object;
}
}
假设还是需要事务增强,那么这个类就必须实现InvocationHandler接口,这个接口中只有一个方法
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
然后实现接口的invoke方法。随后还需要在类中写一个获取代理对象的方法(不在这儿写也可以,获取的方法一样)。有两种方法可以获取代理对象,这里使用Proxy.newProxyInstance来获取。这里需要传入三个参数,一个是类加载器,一个是真实对象类实现的接口,一个就是代理辅助类的对象h。我们先看看Proxy的源码
public class Proxy implements java.io.Serializable { protected InvocationHandler h; public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } Class> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction
protected InvocationHandler h; public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } Class> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } } () { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } }
主要看颜色加深部分,可以看见源码中的Proxy维护了一个InvocationHandler 对象h,调用newProxyInstance方法的时候会先检查辅助类TranscantionManagerHandler对象h是否为空,如果空,则抛出空指针。之后利用传进来的接口和类加载器获取代理类(代理对象会实现该接口,注意代理类只存在内存中,如需存入硬盘需要改变JVM启动参数,方法自行百度)。随后根据代理类的含有InvocationHandler 对象h的构造器创建一个代理类的对象返回。之后我们看看测试方法。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class JDKProxyTest {
@Autowired
private TranscantionManagerHandler handle;
@Test
public void testSave() throws Exception {
IEmployeeService service = handle.getProxyObj();//得到的service为代理对象,代理类存在内存中
service.save(new Employee());
System.out.println("service对象的类是=======》"+service.getClass());
}
}
通过注入代理辅助类对象调用 得到代理对象的方法getProxyObj() 得到代理对象。之后代理对象调用自身的业务方法(这里是save),很多人看到这里就很好奇,辅助类的TranscantionManagerHandler增强的invoke方法什么时候调用的呢?先看看测试结果
service确实是代理类的对象,业务方法也得到了增强,而在代理辅助类invoke方法中的参数method可以看见是接口中的save方法。
需要知道什么时候调用的invoke就需要反编译生成的代理类字节码(调整虚拟机参数将代理类字节码保存在硬盘),在生成代理类中$Proxy11继承了Proxy类,实现了需要被代理类的接口,也就是IEmployeeService,其构造器是如下,还含有三个Object的方法和真实对象的业务方法。
public $Proxy11(InvocationHandler invocationhandler) {
super(invocationhandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("***.RealSubject").getMethod("save", //代理方法名称
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} //static
public final void save() { //代理方法
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
在这儿就可以看出了,当代理对象调用save方法时,会调用
super.h.invoke(this, m3, null);
而在父类Proxy中之前我们讲过它维护了一个InvocationHandler的对象h,因此由于代理辅助类实现了一个InvocationHandler的接口,所以最终h会调用其实现类TranscantionManagerHandler的invoke方法,因此在
Object object = method.invoke(target, args);
前后就可以做想要的增强了。
JDK动态代理,含有增强方法的类需要实现InvocationHandler接口,重写invoke方法。获取代理对象的时候需要传入接口,类加载器,因此需要代理的类必须实现接口。在内存中创建代理类的时候,通过传的接口,创建一个代理类并实现该传入的接口,继承Proxy,在生成的代理类的业务方法中调用父类维护的InvocationHandler的h对象,并调用h.invoke()方法,而由于含有增强方法的代理辅助类实现了InvocationHandler接口,因此实际就会调用代理辅助类实现的invoke方法,最后通过代理辅助类里面注入的真实对象使用method.invoke(target,args)就可以调用真实的业务方法了,在在方法前后就可以做增强。
注意:jdk动态代理必须要实现接口
原理大部分和jdk动态代理一样,只是获取代理对象的方法不同,其余都一样。
public class TranscantionManagerHandlerCallbck implements org.springframework.cglib.proxy.InvocationHandler {
@Setter
private Object target;// 真实对象
@SuppressWarnings("unchecked")
public T getProxyObj() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());//代理类需要继承的父类
enhancer.setCallback(this);//需要增强的对象,即该类的对象
return (T) enhancer.create();
}
/**
* 在真实对象上增强
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("辅助类中的method是:" + method + "===args:" + args);
System.out.println("辅助类中的args是:" + args);
System.out.println("========前面做======增强");
Object object = method.invoke(target, args);
System.out.println("========后面做======增强");
return object;
}
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());//代理类需要继承的父类
enhancer.setCallback(this);//需要增强的对象,即该类的对象
return (T) enhancer.create();
}
/**
* 在真实对象上增强
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("辅助类中的method是:" + method + "===args:" + args);
System.out.println("辅助类中的args是:" + args);
System.out.println("========前面做======增强");
Object object = method.invoke(target, args);
System.out.println("========后面做======增强");
return object;
}
}
这里实现的类是spring的cglib包里的。另外需要cglib代理的类必须是可扩展的,因为cglib代理不需要实现接口,只需要继承父类就可以(继承需要被代理的类)。
先贴一段aop的配置
(有很多配法)在这儿
1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了
2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB