切面是个抽象词,分3个步骤回答。
1.到底是切什么呢?
切方法,把一连串的方法切开。
下面我举例来说明:
1).定义个接口
public interface IAopDemo {
void eat();
void work();
void sleep();
}
2).定义实现类
public class AopDemo implements IAopDemo{
@Override
public void eat() {
System.out.println("eat");
}
@Override
public void work() {
System.out.println("work");
}
@Override
public void sleep() {
System.out.println("sleep");
throw new RuntimeException("exception in sleep Method");
}
}
3).编写业务逻辑代码
@Test
public void testAopDemo1() {
AopDemo aopDemo = new AopDemo();
aopDemo.eat();
aopDemo.work();
aopDemo.sleep();
}
这里业务逻辑是以此执行三个方法,如果按照aop方式执行这三个方法,也就是将这三个方法切开。试想下,用刀从左到右将代码切为4段
@Test
public void testAopDemo1() {
AopDemo aopDemo = new AopDemo();
************************************
aopDemo.eat();
************************************
aopDemo.work();
************************************
aopDemo.sleep();
2.切开做什么?
添加代码
对,就是添加代码,在不影响源代码的前提下添加代码。
3.如何添加代码
添加代码有动态代理和CGLIB的方式来实现,这里我以动态代理为例。
1)代理处理实现类
public class AopDemoHandle implements InvocationHandler {
private AopDemo aopDemo = new AopDemo();
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
Object invoke = null;
try {
beforeExecuteMethod(method);
invoke = method.invoke(aopDemo, args);
afterExecuteMethod(method);
} catch (Exception e) {
executeMethodCaught(e);
}
return invoke;
}
private void executeMethodCaught(Exception e) {
System.out.println("execute method happened exception");
}
private void afterExecuteMethod(Method method) {
System.out.println("after execute " + method.getName());
}
private void beforeExecuteMethod(Method method) {
System.out.println("before execute " + method.getName());
}
}
在方法执行前后,添加处理逻辑代码。
在方法执行异常,添加处理逻辑代码。
2)编写测试案例
@Test
public void testAopDemo2() {
IAopDemo aopDemo = (IAopDemo) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IAopDemo.class}, new AopDemoHandle());
aopDemo.eat();
aopDemo.work();
aopDemo.sleep();
}
执行结果如下:
before execute eat
eat
after execute eat
before execute work
work
after execute work
before execute sleep
sleep
execute method happened exception
至此,aop编程就实现了。切这个词,侧重于从侧面切,不正面切即从正面修改代码,换个维度,换个方式来修改代码。如果题主不能明白,那就把代码写在纸上,用剪刀剪开,自行体会体会。
aop说白了就是我想改代码,但是想用更优雅的方式来修改。
IOC(Inversion of Control)控制反转模式,意味着去除所有有该类产生但不由该类直接负责的对象实例,而改由外界传入。
由简单的对象开发模式到IOC(控制翻转)
1. 依赖注入(Dependency Injection)
组件之间的依赖关系有容器在运行时期决定。
2. 控制反转(IoC. Inversion of Control Containers)
控制权由应用代码中转移到外部容器,控制权的转移。
组件之间的依赖关系有容器在运行时期决定。
2. 控制反转(IoC. Inversion of Control Containers)
控制权由应用代码中转移到外部容器,控制权的转移。
控制反转模式是当今J2EE架构中常用到的模式之一,它符合好莱坞法则:不要调用我,我会调用你。在没有运用IOC的时候,我们一般都是通过工厂来管理对 象,当我们需要一个对象的时候,我们通过工厂来创建它,这样就造成了业务代码和工厂的耦合,并且更重要的是需要我们自己来管理对象的生命周期,这样非常繁 琐,所以如果我们运用IOC的话,不仅可以解除业务代码与工厂的耦合,而且不用我们进行生命周期管理,大大的减少了编码的工作量。如何实现IOC,现在大 概有以下两种方式:
第一:查找实现。此种实现方法需要一个注册表,当我们需要什么对象的时候,我们就去注册表里查找,不需要自己去创建。因为需要的对象是框架或者是容器帮我 们管理的,这时就不需要我们来负责对象的生存周期等的问题。所以生存周期管理也就成了一个容器的必备的能力之一。比如EJB,它就是通过JNDI来查找我 们需要的对象的。这样虽然使得我们不需要管理对象的生命周期,但是同时我们的业务代码还是和具体的注册表API相耦合,所以此种办法没有完全解耦。为了实 现完全的POJO编程模型,需要采用以下的IOC方式(DI)。
第二:依赖注射(DI),目前比较流行的是此种实现。依赖注射要求我们只需要声明要用到什么样子的对象,然后设置JavaBean的setter方法就 OK了,在我们需要用到对象的时候,容器会帮助我们把需要的对象设置进来。或者也可以通过有参构造器来进行注射,通过依赖注射,我们的业务代码就不需要与 具体的容器或者是框架耦合,我们可以完全采用POJO的编程模型。因为我们的业务代码没有与任何的容器相耦合,这样就可以使得代码可以在容器内或者容器外 都可以运行,从而提高了复用性和可移植性,同时我们的测试也很容易实现。由此可见IOC给我们的开发带来的是革命性的变化。使得我们的业务代码与具体的容 器的耦合度降到了最低的同时,也给我们的测试工作带来了便利。
第一:查找实现。此种实现方法需要一个注册表,当我们需要什么对象的时候,我们就去注册表里查找,不需要自己去创建。因为需要的对象是框架或者是容器帮我 们管理的,这时就不需要我们来负责对象的生存周期等的问题。所以生存周期管理也就成了一个容器的必备的能力之一。比如EJB,它就是通过JNDI来查找我 们需要的对象的。这样虽然使得我们不需要管理对象的生命周期,但是同时我们的业务代码还是和具体的注册表API相耦合,所以此种办法没有完全解耦。为了实 现完全的POJO编程模型,需要采用以下的IOC方式(DI)。
第二:依赖注射(DI),目前比较流行的是此种实现。依赖注射要求我们只需要声明要用到什么样子的对象,然后设置JavaBean的setter方法就 OK了,在我们需要用到对象的时候,容器会帮助我们把需要的对象设置进来。或者也可以通过有参构造器来进行注射,通过依赖注射,我们的业务代码就不需要与 具体的容器或者是框架耦合,我们可以完全采用POJO的编程模型。因为我们的业务代码没有与任何的容器相耦合,这样就可以使得代码可以在容器内或者容器外 都可以运行,从而提高了复用性和可移植性,同时我们的测试也很容易实现。由此可见IOC给我们的开发带来的是革命性的变化。使得我们的业务代码与具体的容 器的耦合度降到了最低的同时,也给我们的测试工作带来了便利。