SpringIOC,DI+dynamic proxy 实现盗版AOP

来源:互联网 发布:ps for mac如何破解 编辑:程序博客网 时间:2024/05/19 13:28

SpringIOC,DI+dynamic proxy 实现盗版AOP

什么是Spring IOC:

[1]IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
  其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

什么是DI:
[1]IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。


什么是dynamic proxy:
一般常见的动态代理分两种,一种JDK动态代理,一种CGLIB动态代理,本例中使用的是JDK的动态代理。不贴解释了,对这两个概念不清楚的朋友可以百度一下。


什么是AOP:
面向切面编程(也叫面向方面):Aspect Oriented Programming(AOP),是目前软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。   AOP是OOP的延续,是(Aspect Oriented Programming)的缩写,意思是面向切面(方面)编程。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。


以上都是网上搜的对这些概念的解释,这些文嗖嗖的东西,我一个理科生还真是写不出来,以我个人的理解来看(纯属个人理解,有问题欢迎拍砖)不管是IOC,DI,还是AOP都不只是Sping的东西,它们都是一种相应的概念,一种编程手段,Spring只是实现了它们而已。IOC(控制翻转),就是Sping框架帮你new对象,然后放到它维护的容器中,在Sping容器new对象的过程中,某些对象需要使用带参的构造方法,或者是需要给这些对象设置属性。这就引出了DI(依赖注入)的概念。就是讲字符串或者对象引用赋值给容器维护的对象。


至于AOP(面向切面编程)要实现的目的就是:在编程阶段各个模块完全不用去考虑除核心业务以外的事情,然后在不改动任何源码的情况下,将各个模块组合成完整的业务。例如:持久化数据不需要考虑事务,不需要考虑安全,不需要考虑权限,不需要考虑日志。通过AOP技术可以将这些分模块组合成一个完整的带有日志功能,安全检测,事务开启关闭操作的CRUD业务。


下边来看看这篇blog的重点:



首先先定义一个Bean类:

<span style="font-family: Arial, Helvetica, sans-serif;">public class Person {</span>
public Person() {System.out.println("person obj is create!");}private Long peId;private String name;private Date birthday;get....   set.....}
定义PersonDao接口:

public interface PersonDao {public void savePerson(Person person);public void update(Person person);}

定义实现:

public class PersonDaoImpl implements PersonDao {/** * JoinPoint *  * @param person * @timestamp Dec 5, 2015 4:12:40 PM */@Overridepublic void savePerson(Person person) {System.out.println("save person -->" + person);}@Overridepublic void update(Person person) {System.out.println("update person -->" + person);}}
定义事务类:

public class Transactional{public void begin() {System.out.println("transactional begin!");}public void commit() {System.out.println("transactional commit!");}}
由于dynamic proxy需要一个实现Invocation接口的类所以在定义一个实现Invocation接口的类:

public class Interceptor implements InvocationHandler {private PersonDao personDao;private Transactional transactional;//inject personDao and transaction instancepublic Interceptor(PersonDao personDao, Transactional transactional) {xsuper();this.personDao = personDao;this.transactional = transactional;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 1->if too long<span style="color:#ff0000;">【1】</span>if (method.getName().equals("savePerson")) {
<span style="white-space:pre"></span>//<span style="color:#ff0000;">【2】</span>//invocate begin method of transactional before save person
transactional.begin();method.invoke(personDao, args);//invocate commit method of transactional after save persontransactional.commit();}return null;}}
重要步骤都有注释,可以看一下注释,本人英文水平有限,但是喜欢英文注释,写的不好,小伙伴们受累了。

现在可以看看实现了:

<span style="white-space:pre"></span>@Testpublic void test() {// initialize personPerson p = new Person();p.setBirthday(new Date());p.setName("tom");p.setPeId(132L);PersonDao personDaoImpl = new PersonDaoImpl();// create transactionTransactional trans = new Transactional();//create interceptor<span style="color:#ff0000;">【3】</span>Interceptor in = new Interceptor(personDaoImpl, trans);// dynamic proxyPersonDao pd = (PersonDao) Proxy.newProxyInstance(Person.class.getClassLoader(),//PersonDaoImpl.class.getInterfaces(), in);// invokepd.savePerson(p);}
嘻嘻,执行结果:

person obj is create!transactional begin!save person -->Person [peId=132, name=tom, birthday=Mon Dec 07 23:08:21 CST 2015]transactional commit!

在save person之前开启了事务,save person之后调用了commit 我的这篇博文是不是该结束了呢。。偷笑开个玩笑,重点才刚刚开始。

首先我们可以看看上边代码的不足之处(根据序号定位上述代码):

【1】现在只有一个savePerson方法,如果有10个,100个,1000个?所以if判断不靠谱

【2】transaction方法写死到了Invocation中,这严重违背了AOP的设计原则

【3】如果要扩展模块比如要加一个日志模块,就要改bootstrap代码,这样也不行

怎么改呢?

首先定义一个接口:

public interface Filter {/** * run this method before target method invoke *  * @timestamp Dec 7, 2015 11:27:45 PM */public abstract void after();/** * run this method after target method invoke *  * @timestamp Dec 7, 2015 11:27:53 PM */public abstract void before();}
定义一个FilterChain实现该接口:

public abstract class FilterChain implements Filter {//container of FilterChain instanceprivate List<FilterChain> filterChainList = new ArrayList<>();//judge whether interceptor over according this index private int index = 0;public FilterChain addProp(FilterChain filterChain) {filterChainList.add(filterChain);return this;}public void doFilter(FilterChain filterChain, Method method, Object target, Object[] args) {if (filterChain.index == filterChain.filterChainList.size()) {try {method.invoke(target, args);} catch (Exception e) {throw new RuntimeException(e);}} else {FilterChain f = filterChain.filterChainList.get(filterChain.index);filterChain.index++;f.before();f.doFilter(filterChain, method, target, args);f.after();}}@Overridepublic abstract void after();@Overridepublic abstract void before();public List<FilterChain> getFilterChainList() {return filterChainList;}public void setFilterChainList(List<FilterChain> filterChainList) {this.filterChainList = filterChainList;}}

由于这个类是个核心类,所以有必要解释一下,filterChianList用来装继承了FilterChain类的实例,下边是一个索引初始值为0,运行时通过构造函数或者set方法将继承了FilterChain类的实例传入,并保存在FilterChainList容器中。当目标方法被调用时会执行doFilter方法,该方法会根据index在FilterChainList中获取实例,并将index+1,然后执行实例的before方法,然后是这个实例的doFilter方法,与开始不同的是index加了1下次再取实例时会去取容器的下一个,如此迭代执行直到index与容器的size相同时结束迭代,执行目标方法,最后执行各个实例的after方法。

定义transactional类,由于之前我们已经定义了一个transactional类,不想再写了,那么适配器模式就有用武之地了,看代码:

public class TransactionalAdapter extends FilterChain {Transactional trans = null;public TransactionalAdapter(Transactional trans) {super();this.trans = trans;}/** * After advice(后置通知) *  * @timestamp Dec 5, 2015 4:16:36 PM */@Overridepublic void after() {trans.commit();}/** * Before advice(前置通知) *  * @timestamp Dec 5, 2015 4:08:41 PM */@Overridepublic void before() {trans.begin();}}

然后是修改后的Interceptor

public class Interceptor2 implements InvocationHandler {/** * Target Object(目标对象) */private PersonDao personDao = null;private FilterChain filterChain = null;private Pattern[] patterns;/** *  * @param personDao * @param filterChain * @param regular *            Pointcut(切入点) * @timestamp Dec 5, 2015 4:13:59 PM * @author smallbug */public Interceptor2(PersonDao personDao, FilterChain filterChain, String[] regular) {super();this.personDao = personDao;this.filterChain = filterChain;this.patterns = new Pattern[regular.length];int i = 0;for (String s : regular) {this.patterns[i++] = Pattern.compile(s);}}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();Matcher[] matchers = new Matcher[this.patterns.length];int i = 0;for (Pattern p : this.patterns) {matchers[i++] = p.matcher(methodName);}boolean flag = true;for (Matcher m : matchers) {if (m.find()) {flag = false;filterChain.doFilter(filterChain, method, personDao, args);break;}}if (flag) {method.invoke(personDao, args);}return null;}}

与之前Interceptor不同的是加入了切入点的概念,在调用目标方法的地方掉了doFilter方法

我们再多定义两个模块分别是安全模块和日志模块

public class SecurityFilter extends FilterChain {@Overridepublic void after() {}@Overridepublic void before() {System.out.println("security inspect!");}}


public class LogFilter extends FilterChain {@Overridepublic void after() {System.out.println("person have been saved");}@Overridepublic void before() {System.out.println("person will be save!");}}

这次初始化过程不再硬编码到代码中了,要用sping来装配这些对象

<bean id="personDao" class="cn.smallbug.dynamicproxy.PersonDaoImpl"></bean><bean id="transactional" class="cn.smallbug.dynamicproxy.Transactional"></bean><bean id="transactionalAdapter" class="cn.smallbug.dynamicproxy.TransactionalAdapter"><constructor-arg index="0" ref="transactional"></constructor-arg></bean><bean id="logFilter" class="cn.smallbug.dynamicproxy.LogFilter"></bean><bean id="securityFilter" class="cn.smallbug.dynamicproxy.SecurityFilter"></bean><bean id="filterChian" class="cn.smallbug.dynamicproxy.LogFilter"><property name="filterChainList"><list><ref bean="securityFilter" /><ref bean="logFilter" /><ref bean="transactionalAdapter" /></list></property></bean><bean id="interceptor2" class="cn.smallbug.dynamicproxy.Interceptor2"><constructor-arg index="0" ref="personDao"></constructor-arg><constructor-arg index="1" ref="filterChian"></constructor-arg><constructor-arg index="2"><list><value>^save*</value><value>^update*</value></list></constructor-arg></bean>
再看bootstrap

<span style="white-space:pre"></span>@Testpublic void test3() {// initialize personPerson p = new Person();p.setBirthday(new Date());p.setName("tom笑话");p.setPeId(132L);ApplicationContext aContext = new ClassPathXmlApplicationContext("myaop.xml");PersonDao personDaoImpl = (PersonDao) aContext.getBean("personDao");Interceptor2 in = (Interceptor2) aContext.getBean("interceptor2");PersonDao pd = (PersonDao) Proxy.newProxyInstance(Person.class.getClassLoader(),//personDaoImpl.getClass().getInterfaces(), in);//pd.savePerson(p);pd.update(p);}


输出:

security inspect!person will be save!transactional begin!update person -->Person [peId=132, name=tom, birthday=Tue Dec 08 00:02:01 CST 2015]transactional commit!person have been saved


这样便实现了一个极其简陋的盗版的AOP,如果我们不想使用安全框架了,只需在sping的配置文件中将其对应的bean和注入删除即可,同理需要加什么功能只需继承FilterChian实现before和after方法,然后在配置文件中配置相应的bean,注入到filterChianList中,即可成功运行。


由于个人水平有限,代码逻辑比较混乱,写的也不太容易理解,提供源码下载地址:http://download.csdn.net/detail/qq_21109477/9335247   这种实现也有N多问题,比如真正的AOP是不需要切面去实现任何接口或抽象类的,通知的种类和功能也非常有限,bootstrap中出现了动态代理等。欢迎拍砖,不用给我留情面奋斗奋斗奋斗







1. 孤傲苍狼. 谈谈对Spring IOC的理解. 2015-01-26; Available from: http://www.cnblogs.com/xdp-gacl/p/4249939.html.

0 0
原创粉丝点击