Spring中的AOP
Spring中的AOP
何为AOP
AOP,面向切面编程。
在不改动代码的前提下,灵活的在现有代码的执行顺序前后,添加进新规机能。
来一个简单的Sample:
目标类:
[java]- package com.hyron.tony;
- public class CustomerService {
- private String name;
- private String url;
- public void setName(String name) {
- this.name = name;
- }
- public void setUrl(String url) {
- this.url = url;
- }
- public void printName() {
- System.out.println("Customer name : " + this.name);
- }
- public void printURL() {
- System.out.println("Customer website : " + this.url);
- }
- public void printThrowException() {
- throw new IllegalArgumentException();
- }
- }
advice:只以Around advice为例
[java]- import java.util.Arrays;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- public class HijackAroundMethod implements MethodInterceptor {
- @Override
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- System.out.println("Method name : "
- + methodInvocation.getMethod().getName());
- System.out.println("Method arguments : "
- + Arrays.toString(methodInvocation.getArguments()));
- // same with MethodBeforeAdvice
- System.out.println("HijackAroundMethod : Before method hijacked!");
- try {
- // proceed to original method call
- Object result = methodInvocation.proceed();
- // same with AfterReturningAdvice
- System.out.println("HijackAroundMethod : Before after hijacked!");
- return result;
- } catch (IllegalArgumentException e) {
- // same with ThrowsAdvice
- System.out
- .println("HijackAroundMethod : Throw exception hijacked!");
- throw e;
- }
- }
- }
编织切入关系的配置文件:
[html]- <bean id="customerService" class="com.mkyong.customer.services.CustomerService">
- <property name="name" value="Yong Mook Kim" />
- <property name="url" value="http://www.mkyong.com" />
- </bean>
- <bean id="hijackAroundMethodBean" class="com.mkyong.aop.HijackAroundMethod" />
- <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="target" ref="customerService" />
- <property name="interceptorNames">
- <list>
- <value>hijackAroundMethodBean</value>
- </list>
- </property>
- </bean>
Sample的启动:
[java]- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.mkyong.customer.services.CustomerService;
- public class App {
- public static void main(String[] args) {
- ApplicationContext appContext = new ClassPathXmlApplicationContext(
- new String[] { "Spring-Customer.xml" });
- CustomerService cust = (CustomerService) appContext
- .getBean("customerServiceProxy");
- System.out.println("*************************");
- cust.printName();
- System.out.println("*************************");
- cust.printURL();
- System.out.println("*************************");
- try {
- cust.printThrowException();
- } catch (Exception e) {
- }
- }
- }
以上代码,用customerServiceProxy代理CustomerService的执行
在customerServiceProxy的配置中,定义了用hijackAroundMethodBean作为方法拦截器,在hijackAroundMethodBean中利用invoke方法,拦截住所有的方法调用,塞入自己的逻辑业务。
AOP的两种实现
上面看到的是Spring的Sample。
其实,Spring的AOP也是调用了其他开源技术实现。
比较常用的是JDK自己的Proxy,和开源的CGLIB
两者的区别,Proxy需要Advice必须从接口继承过来。如果切入的目标物是实体类,则无法使用。
CGLIB则可以用于直接覆盖实体类的方法。
Spring对以上两种都有支持。
Spring的底层实现
Spring在配置文件中,通过ProxyFactoryBean编织和实现了切面的构成。
我们在执行以下这行话的时候
CustomerService cust = (CustomerService) appContext
.getBean("customerServiceProxy");
其实是将动态对象的生成委托给了ProxyFactoryBean
当配置文件中 <bean>的class属性配置的实现类是FactoryBean时,通过getBean方法返回的不是FactoryBean本身,而是 FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方 法。
执行顺序如下:
1. ProxyFactoryBean中的getObject
[java]- /**
- * Return a proxy. Invoked when clients obtain beans from this factory bean.
- * Create an instance of the AOP proxy to be returned by this factory.
- * The instance will be cached for a singleton, and create on each call to
- * <code>getObject()</code> for a proxy.
- * @return a fresh AOP proxy reflecting the current state of this factory
- */
- public Object getObject() throws BeansException {
- initializeAdvisorChain();
- if (isSingleton()) {
- return getSingletonInstance();
- }
- else {
- if (this.targetName == null) {
- logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
- "Enable prototype proxies by setting the 'targetName' property.");
- }
- return newPrototypeInstance();
- }
- }
2. ProxyFactoryBean中的initializeAdvisorChain
从配置文件中的advice list中取得interceptorNames,并将其加入advisorChain
[java] view plaincopyprint?- for (String name : this.interceptorNames) {
- if (logger.isTraceEnabled()) {
- logger.trace("Configuring advisor or advice '" + name + "'");
- }
- if (name.endsWith(GLOBAL_SUFFIX)) {
- if (!(this.beanFactory instanceof ListableBeanFactory)) {
- throw new AopConfigException(
- "Can only use global advisors or interceptors with a ListableBeanFactory");
- }
- addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
- name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
- }
|
评论暂时关闭