转载自 http://www.cnblogs.com/digdeep/p/4528353.html 多谢@digdeep

AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。这些系统性的编程工作都可以独立编码实现,然后通过AOP技术切入进系统即可。从而达到了将不同的关注点分离出来的效果。本文深入剖析Spring的AOP的原理。

1. AOP相关的概念

1)Aspect:切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面;

2)Join point:连接点,也就是可以进行横向切入的位置;

3)Advice:通知,切面在某个连接点执行的操作(分为:Before advice,After returning advice,After throwing advice,After (finally) advice,Around advice);

4)Pointcut:切点,符合切点表达式的连接点,也就是真正被切入的地方;

2. AOP 的实现原理

AOP分为静态AOP和动态AOP。静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。动态AOP是指将切面代码进行动态织入实现的AOP。Spring的AOP为动态AOP,实现的技术为:JDK提供的动态代理技术  CGLIB(动态字节码增强技术)。尽管实现技术不一样,但都是基于代理模式,都是生成一个代理对象。

1) JDK动态代理

主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。JDK动态代理要求被代理实现一个接口,只有接口中的方法才能够被代理。其方法是将被代理对象注入到一个中间对象,而中间对象实现InvocationHandler接口,在实现该接口时,可以在 被代理对象调用它的方法时,在调用的前后插入一些代码。而 Proxy.newProxyInstance() 能够利用中间对象来生产代理对象。插入的代码就是切面代码。所以使用JDK动态代理可以实现AOP。我们看个例子:

被代理对象实现的接口,只有接口中的方法才能够被代理:

  1. public interface UserService {
  2. public void addUser(User user);
  3. public User getUser(int id);
  4. }

被代理对象:

  1. public class UserServiceImpl implements UserService {
  2. public void addUser(User user) {
  3. System.out.println("add user into database.");
  4. }
  5. public User getUser(int id) {
  6. User user = new User();
  7. user.setId(id);
  8. System.out.println("getUser from database.");
  9. return user;
  10. }
  11. }

代理中间类:

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3.  
  4. public class ProxyUtil implements InvocationHandler {
  5. private Object target; // 被代理的对象
  6.  
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. System.out.println("do sth before....");
  9. Object result = method.invoke(target, args);
  10. System.out.println("do sth after....");
  11. return result;
  12. }
  13. ProxyUtil(Object target){
  14. this.target = target;
  15. }
  16. public Object getTarget() {
  17. return target;
  18. }
  19. public void setTarget(Object target) {
  20. this.target = target;
  21. }
  22. }

测试:

  1. import java.lang.reflect.Proxy;
  2. import net.aazj.pojo.User;
  3.  
  4. public class ProxyTest {
  5. public static void main(String[] args){
  6. Object proxyedObject = new UserServiceImpl(); // 被代理的对象
  7. ProxyUtil proxyUtils = new ProxyUtil(proxyedObject);
  8.  
  9. // 生成代理对象,对被代理对象的这些接口进行代理:UserServiceImpl.class.getInterfaces()
  10. UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
  11. UserServiceImpl.class.getInterfaces(), proxyUtils);
  12. proxyObject.getUser(1);
  13. proxyObject.addUser(new User());
  14. }
  15. }

执行结果:

  1. do sth before....
  2. getUser from database.
  3. do sth after....
  4. do sth before....
  5. add user into database.
  6. do sth after....

我们看到在 UserService接口中的方法addUser 和 getUser方法的前面插入了我们自己的代码。这就是JDK动态代理实现AOP的原理。

我们看到该方式有一个要求,被代理的对象必须实现接口,而且只有接口中的方法才能被代理。

2)CGLIB(code generate libary)

字节码生成技术实现AOP,其实就是继承被代理对象,然后Override需要被代理的方法,在覆盖该方法时,自然是可以插入我们自己的代码的。因为需要Override被代理对象的方法,所以自然CGLIB技术实现AOP时,就必须要求需要被代理的方法不能是final方法,因为final方法不能被子类覆盖。我们使用CGLIB实现上面的例子:

  1. package net.aazj.aop;
  2.  
  3. import java.lang.reflect.Method;
  4. import net.sf.cglib.proxy.Enhancer;
  5. import net.sf.cglib.proxy.MethodInterceptor;
  6. import net.sf.cglib.proxy.MethodProxy;
  7.  
  8. public class CGProxy implements MethodInterceptor{
  9. private Object target; // 被代理对象
  10. public CGProxy(Object target){
  11. this.target = target;
  12. }
  13. public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
  14. System.out.println("do sth before....");
  15. Object result = proxy.invokeSuper(arg0, arg2);
  16. System.out.println("do sth after....");
  17. return result;
  18. }
  19. public Object getProxyObject() {
  20. Enhancer enhancer = new Enhancer();
  21. enhancer.setSuperclass(this.target.getClass()); // 设置父类
  22. // 设置回调
  23. enhancer.setCallback(this); // 在调用父类方法时,回调 this.intercept()
  24. // 创建代理对象
  25. return enhancer.create();
  26. }
  27. }
  1. public class CGProxyTest {
  2. public static void main(String[] args){
  3. Object proxyedObject = new UserServiceImpl(); // 被代理的对象
  4. CGProxy cgProxy = new CGProxy(proxyedObject);
  5. UserService proxyObject = (UserService) cgProxy.getProxyObject();
  6. proxyObject.getUser(1);
  7. proxyObject.addUser(new User());
  8. }
  9. }

输出结果:

  1. do sth before....
  2. getUser from database.
  3. do sth after....
  4. do sth before....
  5. add user into database.
  6. do sth after....

我们看到达到了同样的效果。它的原理是生成一个父类enhancer.setSuperclass(this.target.getClass())的子类enhancer.create(),然后对父类的方法进行拦截enhancer.setCallback(this). 对父类的方法进行覆盖,所以父类方法不能是final的。

3)接下来我们看下spring实现AOP的相关源码:

  1. @SuppressWarnings("serial")
  2. public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
  3. @Override
  4. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  5. if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
  6. Class<?> targetClass = config.getTargetClass();
  7. if (targetClass == null) {
  8. throw new AopConfigException("TargetSource cannot determine target class: " +
  9. "Either an interface or a target is required for proxy creation.");
  10. }
  11. if (targetClass.isInterface()) {
  12. return new JdkDynamicAopProxy(config);
  13. }
  14. return new ObjenesisCglibAopProxy(config);
  15. }
  16. else {
  17. return new JdkDynamicAopProxy(config);
  18. }
  19. }

从上面的源码我们可以看到:

  1. if (targetClass.isInterface()) {
  2. return new JdkDynamicAopProxy(config);
  3. }
  4. return new ObjenesisCglibAopProxy(config);

如果被代理对象实现了接口,那么就使用JDK的动态代理技术,反之则使用CGLIB来实现AOP,所以Spring默认是使用JDK的动态代理技术实现AOP的。

JdkDynamicAopProxy的实现其实很简单:

  1. final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  2. @Override
  3. public Object getProxy(ClassLoader classLoader) {
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
  6. }
  7. Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
  8. findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  9. return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
  10. }

3. Spring AOP的配置

Spring中AOP的配置一般有两种方法,一种是使用 <aop:config> 标签在xml中进行配置,一种是使用注解以及@Aspect风格的配置。

1)基于<aop:config>的AOP配置

下面是一个典型的事务AOP的配置:

  1. <tx:advice id="transactionAdvice" transaction-manager="transactionManager"?>
  2. <tx:attributes >
  3. <tx:method name="add*" propagation="REQUIRED" />
  4. <tx:method name="append*" propagation="REQUIRED" />
  5. <tx:method name="insert*" propagation="REQUIRED" />
  6. <tx:method name="save*" propagation="REQUIRED" />
  7. <tx:method name="update*" propagation="REQUIRED" />
  8.  
  9. <tx:method name="get*" propagation="SUPPORTS" />
  10. <tx:method name="find*" propagation="SUPPORTS" />
  11. <tx:method name="load*" propagation="SUPPORTS" />
  12. <tx:method name="search*" propagation="SUPPORTS" />
  13.  
  14. <tx:method name="*" propagation="SUPPORTS" />
  15. </tx:attributes>
  16. </tx:advice>
  17. <aop:config>
  18. <aop:pointcut id="transactionPointcut" expression="execution(* net.aazj.service..*Impl.*(..))" />
  19. <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
  20. </aop:config>

再看一个例子:

  1. <bean id="aspectBean" class="net.aazj.aop.DataSourceInterceptor"/>
  2. <aop:config>
  3. <aop:aspect id="dataSourceAspect" ref="aspectBean">
  4. <aop:pointcut id="dataSourcePoint" expression="execution(public * net.aazj.service..*.getUser(..))" />
  5. <aop:pointcut expression="" id=""/>
  6. <aop:before method="before" pointcut-ref="dataSourcePoint"/>
  7. <aop:after method=""/>
  8. <aop:around method=""/>
  9. </aop:aspect>
  10.  
  11. <aop:aspect></aop:aspect>
  12. </aop:config>

<aop:aspect> 配置一个切面;<aop:pointcut>配置一个切点,基于切点表达式;<aop:before>,<aop:after>,<aop:around>是定义不同类型的advise. aspectBean 是切面的处理bean:

  1. public class DataSourceInterceptor {
  2. public void before(JoinPoint jp) {
  3. DataSourceTypeManager.set(DataSources.SLAVE);
  4. }
  5. }

2) 基于注解和@Aspect风格的AOP配置

我们以事务配置为例:首先我们启用基于注解的事务配置

  1. <!-- 使用annotation定义事务 -->
  2. <tx:annotation-driven transaction-manager="transactionManager" />

然后扫描Service包:

  1. <context:component-scan base-package="net.aazj.service,net.aazj.aop" />

最后在service上进行注解:

  1. @Service("userService")
  2. @Transactional
  3. public class UserServiceImpl implements UserService{
  4. @Autowired
  5. private UserMapper userMapper;
  6.  
  7. @Transactional (readOnly=true)
  8. public User getUser(int userId) {
  9. System.out.println("in UserServiceImpl getUser");
  10. System.out.println(DataSourceTypeManager.get());
  11. return userMapper.getUser(userId);
  12. }
  13.  
  14. public void addUser(String username){
  15. userMapper.addUser(username);
  16. // int i = 1/0; // 测试事物的回滚
  17. }
  18.  
  19. public void deleteUser(int id){
  20. userMapper.deleteByPrimaryKey(id);
  21. // int i = 1/0; // 测试事物的回滚
  22. }
  23.  
  24. @Transactional (rollbackFor = BaseBusinessException.class)
  25. public void addAndDeleteUser(String username, int id) throws BaseBusinessException{
  26. userMapper.addUser(username);
  27. this.m1();
  28. userMapper.deleteByPrimaryKey(id);
  29. }
  30.  
  31. private void m1() throws BaseBusinessException {
  32. throw new BaseBusinessException("xxx");
  33. }
  34.  
  35. public int insertUser(User user) {
  36. return this.userMapper.insert(user);
  37. }
  38. }

搞定。这种事务配置方式,不需要我们书写pointcut表达式,而是我们在需要事务的类上进行注解。但是如果我们自己来写切面的代码时,还是要写pointcut表达式。下面看一个例子(自己写切面逻辑):

首先去扫描 @Aspect 注解定义的 切面:

  1. <context:component-scan base-package="net.aazj.aop" />

启用@AspectJ风格的注解:

  1. <aop:aspectj-autoproxy />

这里有两个属性,<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>, proxy-target-class="true" 这个最好不要随便使用,它是指定只能使用CGLIB代理,那么对于final方法时会抛出错误,所以还是让spring自己选择是使用JDK动态代理,还是CGLIB. expose-proxy="true"的作用后面会讲到。

切面代码:

  1. import org.aspectj.lang.JoinPoint;
  2. import org.aspectj.lang.annotation.Aspect;
  3. import org.aspectj.lang.annotation.Before;
  4. import org.aspectj.lang.annotation.Pointcut;
  5. import org.springframework.core.annotation.Order;
  6. import org.springframework.stereotype.Component;
  7. @Aspect // for aop
  8. @Component // for auto scan
  9. @Order(0) // execute before @Transactional
  10. public class DataSourceInterceptor {
  11. @Pointcut("execution(public * net.aazj.service..*.get*(..))")
  12. public void dataSourceSlave(){};
  13.  
  14. @Before("dataSourceSlave()")
  15. public void before(JoinPoint jp) {
  16. DataSourceTypeManager.set(DataSources.SLAVE);
  17. }
  18. }

我们使用到了 @Aspect 来定义一个切面;@Component是配合<context:component-scan/>,不然扫描不到;@Order定义了该切面切入的顺序,因为在同一个切点,可能同时存在多个切面,那么在这多个切面之间就存在一个执行顺序的问题。该例子是一个切换数据源的切面,那么他应该在 事务处理 切面之前执行,所以我们使用 @Order(0) 来确保先切换数据源,然后加入事务处理。@Order的参数越小,优先级越高,默认的优先级最低:

  1. /**
  2. * Annotation that defines ordering. The value is optional, and represents order value
  3. * as defined in the {@link Ordered} interface. Lower values have higher priority.
  4. * The default value is {@code Ordered.LOWEST_PRECEDENCE}, indicating
  5. * lowest priority (losing to any other specified order value).
  6. */
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
  9. public @interface Order {
  10. /**
  11. * The order value. Default is {@link Ordered#LOWEST_PRECEDENCE}.
  12. * @see Ordered#getOrder()
  13. */
  14. int value() default Ordered.LOWEST_PRECEDENCE;
  15. }

关于数据源的切换可以参加专门的博文:http://www.cnblogs.com/digdeep/p/4512368.html

3)切点表达式(pointcut)

上面我们看到,无论是 <aop:config> 风格的配置,还是 @Aspect 风格的配置,切点表达式都是重点。都是我们必须掌握的。

 1> pointcut语法形式(execution):

  1. execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

带有 ? 号的部分是可选的,所以可以简化成:ret-type-pattern name-pattern(param_pattern) 返回类型,方法名称,参数三部分来匹配。

配置起来其实也很简单: * 表示任意返回类型,任意方法名,任意一个参数类型; .. 连续两个点表示0个或多个包路径,还有0个或多个参数。就是这么简单。看下例子:

execution(* net.aazj.service..*.get*(..)) :表示net.aazj.service包或者子包下的以get开头的方法,参数可以是0个或者多个(参数不限);

execution(* net.aazj.service.AccountService.*(..)): 表示AccountService接口下的任何方法,参数不限;

注意这里,将类名和包路径是一起来处理的,并没有进行区分,因为类名也是包路径的一部分。

参数param-pattern部分比较复杂: () 表示没有参数,(..)参数不限,(*,String) 第一个参数不限类型,第二参数为String.

2> within() 语法:

within()只能指定(限定)包路径(类名也可以看做是包路径),表示某个包下或者子报下的所有方法:

within(net.aazj.service.*), within(net.aazj.service..*),within(net.aazj.service.UserServiceImpl.*)

3> this() 与 target():

this是指代理对象,target是指被代理对象(目标对象)。所以 this() 和 target() 分别限定 代理对象的类型和被代理对象的类型:

this(net.aazj.service.UserService): 实现了UserService的代理对象(中的所有方法);

target(net.aazj.service.UserService): 被代理对象实现了UserService(中的所有方法);

4> args():

限定方法的参数的类型:

args(net.aazj.pojo.User): 参数为User类型的方法。

5> @target(), @within(), @annotation(), @args():

这些语法形式都是针对注解的,比如带有某个注解的,带有某个注解的方法参数的类型带有某个注解:

@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)

两者都是指被代理对象上有 @Transactional 注解的(类的所有方法),(两者似乎没有区别???)

@annotation(org.springframework.transaction.annotation.Transactional): 方法 带有@Transactional 注解的所有方法

@args(org.springframework.transaction.annotation.Transactional):参数的类型 带有@Transactional 注解的所有方法

6> bean(): 指定某个bean的名称

bean(userService): bean的id为 "userService" 的所有方法;

bean(*Service): bean的id为 "Service"字符串结尾的所有方法;

另外注意上面这些表达式是可以利用 ||, &&, ! 进行自由组合的。比如:execution(public * net.aazj.service..*.getUser(..)) && args(Integer,..)

4. 向注解处理方法传递参数

有时我们在写注解处理方法时,需要访问被拦截的方法的参数。此时我们可以使用 args() 来传递参数,下面看一个例子:

  1. @Aspect
  2. @Component // for auto scan
  3. //@Order(2)
  4. public class LogInterceptor {
  5. @Pointcut("execution(public * net.aazj.service..*.getUser(..))")
  6. public void myMethod(){};
  7.  
  8. @Before("myMethod()")
  9. public void before() {
  10. System.out.println("method start");
  11. }
  12.  
  13. @After("myMethod()")
  14. public void after() {
  15. System.out.println("method after");
  16. }
  17.  
  18. @AfterReturning("execution(public * net.aazj.mapper..*.*(..))")
  19. public void AfterReturning() {
  20. System.out.println("method AfterReturning");
  21. }
  22.  
  23. @AfterThrowing("execution(public * net.aazj.mapper..*.*(..))")
  24. // @Around("execution(public * net.aazj.mapper..*.*(..))")
  25. public void AfterThrowing() {
  26. System.out.println("method AfterThrowing");
  27. }
  28.  
  29. @Around("execution(public * net.aazj.mapper..*.*(..))")
  30. public Object Around(ProceedingJoinPoint jp) throws Throwable {
  31. System.out.println("method Around");
  32. SourceLocation sl = jp.getSourceLocation();
  33. Object ret = jp.proceed();
  34. System.out.println(jp.getTarget());
  35. return ret;
  36. }
  37.  
  38. @Before("execution(public * net.aazj.service..*.getUser(..)) && args(userId,..)")
  39. public void before3(int userId) {
  40. System.out.println("userId-----" + userId);
  41. }
  42.  
  43. @Before("myMethod()")
  44. public void before2(JoinPoint jp) {
  45. Object[] args = jp.getArgs();
  46. System.out.println("userId11111: " + (Integer)args[0]);
  47. System.out.println(jp.getTarget());
  48. System.out.println(jp.getThis());
  49. System.out.println(jp.getSignature());
  50. System.out.println("method start");
  51. }
  52. }

方法:

  1. @Before("execution(public * net.aazj.service..*.getUser(..)) && args(userId,..)")
  2. public void before3(int userId) {
  3. System.out.println("userId-----" + userId);
  4. }

它会拦截 net.aazj.service 包下或者子包下的getUser方法,并且该方法的第一个参数必须是int型的,那么使用切点表达式args(userId,..)就可以使我们在切面中的处理方法before3中可以访问这个参数。

before2方法也让我们知道也可以通过 JoinPoint 参数来获得被拦截方法的参数数组。JoinPoint 是每一个切面处理方法都具有的参数,@Around类型的具有的参数类型为ProceedingJoinPoint。通过JoinPoint或者ProceedingJoinPoint参数可以访问到被拦截对象的一些信息(参见上面的before2方法)。

5. Spring AOP的缺陷

因为Spring AOP是基于动态代理对象的,那么如果target中的方法不是被代理对象调用的,那么就不会织入切面代码,看个例子:

  1. @Service("userService")
  2. @Transactional
  3. public class UserServiceImpl implements UserService{
  4. @Autowired
  5. private UserMapper userMapper;
  6.  
  7. @Transactional (readOnly=true)
  8. public User getUser(int userId) {
  9. return userMapper.getUser(userId);
  10. }
  11.  
  12. public void addUser(String username){
  13. getUser(2);
  14. userMapper.addUser(username);
  15. }

看到上面的 addUser() 方法中,我们调用了 getUser() 方法,而getUser() 方法是谁调用的呢?是UserServiceImpl的实例,不是代理对象,那么getUser()方法就不会被织入切面代码。

切面代码如下:

  1. @Aspect
  2. @Component
  3. public class AOPTest {
  4. @Before("execution(public * net.aazj.service..*.getUser(..))")
  5. public void m1(){
  6. System.out.println("in m1...");
  7. }
  8. @Before("execution(public * net.aazj.service..*.addUser(..))")
  9. public void m2(){
  10. System.out.println("in m2...");
  11. }
  12. }

执行如下代码:

  1. public class Test {
  2. public static void main(String[] args){
  3. ApplicationContext context = new ClassPathXmlApplicationContext(
  4. new String[]{"config/spring-mvc.xml","config/applicationContext2.xml"});
  5.  
  6. UserService us = context.getBean("userService", UserService.class);
  7. if(us != null){
  8. us.addUser("aaa");

输出结果如下:

  1. in m2...

虽然 getUser()方法 被调用了,但是因为不是代理对象调用的,所以 AOPTest.m1() 方法并没有执行。这就是Spring aop的缺陷。解决方法如下:

首先: 将 <aop:aspectj-autoproxy /> 改为:

  1. <aop:aspectj-autoproxy expose-proxy="true"/>

然后,修改UserServiceImpl中的 addUser() 方法:

  1. @Service("userService")
  2. @Transactional
  3. public class UserServiceImpl implements UserService{
  4. @Autowired
  5. private UserMapper userMapper;
  6.  
  7. @Transactional (readOnly=true)
  8. public User getUser(int userId) {
  9. return userMapper.getUser(userId);
  10. }
  11.  
  12. public void addUser(String username){
  13. ((UserService)AopContext.currentProxy()).getUser(2);
  14. userMapper.addUser(username);
  15. }
  1. ((UserService)AopContext.currentProxy()).getUser(2); 先获得当前的代理对象,然后在调用 getUser() 方法,就行了。
  2.  
  3. expose-proxy="true" 表示将当前代理对象暴露出去,不然 AopContext.currentProxy() 或得的是 null .
  4.  
  5. 修改之后的运行结果:
  1. in m2...
  2. in m1...

[转载]Spring AOP 深入剖析的更多相关文章

  1. Spring AOP 深入剖析

    AOP是Spring提供的关键特性之一.AOP即面向切面编程,是OOP编程的有效补充.使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统.从而避免了在业务逻 ...

  2. Spring AOP深入剖析

    一.通过代理工厂模式配置通知 ①.前置通知.后置通知: 定义某接口:ISomeService,并自定义方法 public interface ISomeService { public void tr ...

  3. [转载] spring aop 环绕通知around和其他通知的区别

    前言: spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别: 1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知   是不能决定的,他们只 ...

  4. Spring AOP 实现原理与 CGLIB 应用

    https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/ AOP(Aspect Orient Programming),也就是面向 ...

  5. Spring AOP 实现原理与 CGLIB 应用--转

    AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理.安全检查.缓存.对象池管理等.AOP 实现的关键就在于 ...

  6. spring AOP 之一:spring AOP功能介绍

    一.AOP简介 AOP:是一种面向切面的编程范式,是一种编程思想,旨在通过分离横切关注点,提高模块化,可以跨越对象关注点.Aop的典型应用即spring的事务机制,日志记录.利用AOP可以对业务逻辑的 ...

  7. 【转】Spring AOP 实现原理与 CGLIB 应用

    AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理.安全检查.缓存.对象池管理等.AOP 实现的关键就在于 ...

  8. spring AOP详解四

    AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理.安全检查.缓存.对象池管理等.AOP 实现的关键就在于 ...

  9. Spring AOP详解(转载)所需要的包

    上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...

随机推荐

  1. JS面向对象(二)---继承

    一.面向对象的继承 1.解析:在原有对象的基础上,略作修改,得到一个新的对象,并且不影响原有对象的功能 2.如何添加继承---拷贝继承 属性:call 方法: for in /* 继承:子类不影响父类 ...

  2. es6注意点

    补救方法: 详情:http://es6.ruanyifeng.com/#docs/array 取出文本内容 实现深拷贝 jq实现不完全深拷贝 jQuery.extend = jQuery.fn.ext ...

  3. python--常用模块:collections 、time、random

      一.collections 模块 1:nametuple 生成可以用名字访问内容的元祖 from collections import namedtuple point=namedtuple('p ...

  4. Raft——可理解的分布式一致性算法

    Raft  Understandable Distributed Consensus http://thesecretlivesofdata.com/raft/ 一个直观的动画,便于理解raft算法. ...

  5. python3添加requests库

    1.资源下载 https://codeload.github.com/psf/requests/zip/master https://www.python.org/ https://files.pyt ...

  6. linux下设置Git

    目录 ## Git介绍 1.工作原理 2.SVN与Git的最主要的区别? 3.操作 4.创建本地仓库 5.把文件添加到本地仓库 6.版本回退 7.理解工作区(workspace)与暂存区(index) ...

  7. 泛型(Generic)方法(函数,算法)

    例子: static void Main(string[] args) { int[] a1 = { 1, 2, 3, 4, 5 }; int[] a2 = { 1, 2, 3, 4, 5 }; do ...

  8. 区别 |DCL |DDL |DML |DQL

    DCL(Data Control Language)数据控制语言: 用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句.这个比较少用到. 对于大多数人,在公司一 ...

  9. 任意两点间的最短路问题(Floyd-Warshall算法)

    /* 任意两点间的最短路问题(Floyd-Warshall算法) */ import java.util.Scanner; public class Main { //图的顶点数,总边数 static ...

  10. CSS四种定位及应用

    定位(position) 如果,说浮动, 关键在一个 “浮” 字上面, 那么 我们的定位,关键在于一个 “位” 上. PS: 定位是我们CSS算是数一数二难点的了,但是,你务必要学好它,我们CSS离不 ...