AOP:面向切面的开发
1 AOP的概念
1.1 面向对象:继承
继承解决了软件复用(代码复用)的问题,子类自动拥有了父类的各种方法。如果有需要改变的地方,可以覆盖(重写)父类的方法。
比如:A类中有方法a(), 如果B类需要用到a()方法,可以 B extends A
1.2 继承的局限性
用面向对象中继承来解决软件复用问题并不全面。解决不了在方法内部的重复问题
例子:before和after依旧重复出现
class A{
public void a(){
before();
语句1
after();
}
public void b(){
before();
语句2
after();
}
private void before(){
{
//代码块1
}
}
private void after(){
{
// 代码块2
}
}
}
1.3 AOP思想
面向切面(多个方法)的开发。
好处:
- 解决了软件复用的问题
- 在需要给方法a()增加新功能,可以不用动原始代码,只需要在切面把功能切进去。
让类AOP去切a\b两个方法,自动在a\b方法前后加上before和after方法。
class A{
public void a(){
语句1
}
public void b(){
语句2
}
}
class Aop{
public void before(){
{
//代码块1
}
}
public void after(){
{
// 代码块2
}
}
}
2 AOP的实现原理-代理模式
以租房为例:小明准备租房,为了省下时间和精力去找房,所以去找中介租房。中介就把房东的房子租给小明。
房东:real真实的对象
中介:代理
小明:租客租房,相当于测试类
2.1 静态代理
public interface IRent {
void rent();
}
/**
* 房东
* @author Administrator
*
*/
public class HouseKeeper implements IRent {
@Override
public void rent() {// 房东和中介应当都具有租房这个功能
System.out.println("出租豪华公寓");
}
}
/**
* 中介,代理对象
* @author Administrator
*
*/
public class HouseAgent implements IRent {
// 代理模式:代理真实对象想要做的事情
// 中介本身没有房子,只是帮助房东实现rent方法.
private IRent real;
public HouseAgent(IRent real) {// 通过构造方法,将真实的对象传入代理对象
this.real = real;
}
@Override
public void rent() {// 调用真实对象的rent方法
this.real.rent();
}
}
public class TestRent {
@Test
public void test() {// 测试:小明找中介租房
IRent real = new HouseKeeper();// 创建真实对象:房东的房子
IRent agent = new HouseAgent(real);// 创建代理对象:中介拿到房东的房子
agent.rent();// 在小明租房的时候,看不见真实的对象,只是调用了代理对象的方法。但也完成了rent功能
}
}
- 代理模式:代理真实对象想要做的事情
- 中介本身没有房子,只是帮助房东实现rent方法.
- 在小明租房的时候,看不见真实的对象,只是调用了代理对象的方法。但也完成了rent功能
代理模式优点:
- 隐藏real真实对象
- 代理对象的方法可以进行功能修改和添加
比如
代理模式缺点:
- 接口是不可变动的,如果变动,多出的方法不会被before和after切在中间。
- 如果再次将多出来的方法前后也加上before和after,那么又和上面继承存在一样的问题了,因此接口可变时,可以用动态代理
2.2 动态代理
JDK已经实现了动态代理,接口变更之后,切入可以更加接口方法变化切入。
public interface IRent {
void rent();
void job();
}
/**
* 房东
* @author Administrator
*
*/
public class HouseKeeper implements IRent {
@Override
public void rent() {// 房东和中介应当都具有租房这个功能
System.out.println("出租豪华公寓");
}
@Override
public void job() {
System.out.println("找工作");
}
}
/**
* 产生代理对象的类
* @author Administrator
*
*/
public class AgentMaker implements InvocationHandler {
private Object real;
public AgentMaker(IRent real) {// 传入的真实对象
this.real = real;
}
/**
* 产生代理对象
* @return
*/
public IRent createAgent() {
return (IRent) Proxy.newProxyInstance(real.getClass().getClassLoader(),
real.getClass().getInterfaces(),
this);
}
/**
* 调用真实对象的方法(根据反射)
* method就是真实对象的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object retval = method.invoke(this.real, args);// 第一个参数(obj)代表真实对象,第二个参数(args)代表调用method方法需要的参数
after();
return retval;
}
private void before() {
System.out.println("收取押金2000元");
}
private void after() {
System.out.println("每月检查卫生情况,不合格罚款50元");
}
}
public class TestRent {
@Test
public void test() {
IRent real = new HouseKeeper();// 产生真实对象
IRent agentMaker = new AgentMaker(real).createAgent();// 产生代理对象
agentMaker.rent();
agentMaker.job();
}
}
2.3 cglib代理
动态代理的问题:如果真实对象没有实现任何接口则不行
继承机制:子类继承父类的方法,可进行重写
cglib代理需要引入依赖:
/**
* 房东
* @author Administrator
*
*/
public class HouseKeeper{// 真实对象并未实现任何接口
public void rent() {// 房东和中介应当都具有租房这个功能
System.out.println("出租豪华公寓");
}
public void job() {
System.out.println("找工作");
}
}
/**
* 产生代理对象的类
* @author Administrator
*
*/
public class AgentMaker implements MethodInterceptor {
private Object real;
public AgentMaker(Object real) {
this.real = real;
}
public Object creatProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(real.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
private void before() {
System.out.println("收取押金2000元");
}
private void after() {
System.out.println("每月检查卫生情况,不合格罚款50元");
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object retVal = proxy.invokeSuper(obj, args);// 调用父类的方法
after();
return retVal;
}
}
public class TestRent {
@Test
public void test() {
HouseKeeper real = new HouseKeeper();
HouseKeeper creatProxy = (HouseKeeper) new AgentMaker(real).creatProxy();
creatProxy.job();
}
}