Spring IOC与AOP

Spring IOC与AOP

IOC

1.1 Spring容器

分类:

  • Bean工厂,BeanFactory
  • 应用上下文,ApplicationContext,常用

获取ApplicationContext:

通过ClassPathXmlApplicationContext对象来获取

	ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

1.2 创建Bean

Spring中有四种方式配置对象

  • 使用XML文件配置
  • 使用注解配置
  • 使用JavaConfig配置
  • groovy脚本DSL
1.2.1 XML文件配置
  • 无参构造器创建对象
  • 带参构造器创建对象
  • 工厂方法创建对象
    • 静态工厂创建对象
    • 实例工厂创建对象
1.2.1.1 无参构造器创建对象
public Class User {
    private String id;
    private String username;
}

配置ApplicationContext.xml

<bean id="user" class="User"/>

获取bean

	ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
	User user = (User) ac.getBean("user");
1.2.1.2 有参构造器创建对象

JavaBean提供有参构造器

public User(String id, String name, Person person) {
    this.id = id;
    this.name = name;
    this.person = person;
}

配置ApplicationContext.xml

<bean id="person" class="Person"></bean>
<bean id="user" class="User">
	<constructor-arg index="0" name="id" type="java.lang.String" value="1"/>
    <constructor-arg index="1" name="name" type="java.lang.String" value="Eric"/>
    <constructor-arg index="2" name="person" type="Person" ref="person"/>
</bean>
1.2.1.3 静态工厂创建对象

使用静态工厂返回一个对象

public class Factory {
    public static User getBean() {
        return new User();
    }
}

配置文件中使用工厂的静态方法返回对象

<bean id="user" class="Factory" factory-method="getBean">

</bean>
1.2.1.4 实例工厂创建对象

通过工厂类的实例方法获取对象

public class Factory {
    public User getBean() {
        return new User();
    }
}

配置文件中使用工厂的实例方法返回对象

<!-- 先创建工厂对象 -->
<bean id="factory" class="Factroy"></bean>

<!-- 指定工厂对象和工厂方法 -->
<bean id="user" class="User" factory-bean="factory" factory-method="getBean"/>
1.2.2 注解方式创建对象
  1. 先引入context名称空间

    • xmlns:context=“http://www.springframework.org/schema/context”
  2. 开启注解扫描器

    • <context:component-scan base-package=""/>
    • 或通过自定义扫描类并以@CompontentScan注解修饰
    //表名该类是配置类
    @ComponentScan(basePackages = "xxx", useDefaultFilters = true, excludeFilters = {@ConponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
    public class AnnotationScan {
        
    }
    
  3. 相关注解

    @ComponentScan

    @ContextConfiguration

    @Configuration

    @Component

    @Repository

    @Service

    @Controller

    @Resource 依赖关系

    • 若@Resource指定了值,就根据名字来查找
    • 若@Resource没指定值,就根据类型找,同类型Bean在IOC容器中不能有多个
1.2.3 注解方式创建对象
  • 编写Java类,使用@Configuration修饰
  • @Configuration 是一个类级别的注解,被@Configuration修饰的类就是配置类,用来标记被修饰类的对象是一个 BeanDefinition。
  • 使用@Bean注解声明一个bean

编写配置类

@Configuration
public class Configuration {
    
}

测试类需用@ContextConfiguration注解修饰

@ContextConfiguration(classes = Configuration.class)
public class Test {
    //...
}

1.3 Bean属性

scope属性
  • singleton:单实例,实例在IOC容器创建之前被创建。

  • prototype:多实例,在对象被使用时才会创建

lazy-init属性

仅针对singleton对象有效,默认为false,设置后,对象会在被使用时创建

init-method和destory-method方法

分别为对象创建后和销毁前执行相应方法

1.4 依赖注入

  • 构造方法注入
  • setter方法注入
  • 自动装配
  • 注解

AOP

2.1 AOP概念与术语

2.1.1 Aspect(切面)

Aspect由point cut和advice组成,既包含了横切逻辑的定义,也包含了连接点的定义。

  • pointcut:匹配join point的谓词,提供一组规则,针对特定的joinpoint织入Advice
  • Advice(增强):由Aspect添加到特定join point的一段代码

Advice的类型

  • before advice
  • after return advice
  • after throwing advice
  • after(final) advice
  • around advice
2.1.2join point(连接点)

2.2 AOP实现方式

基于动态代理

  • 基于JDK动态代理
  • 基于cglib
2.2.1 基于JDK的动态代理(基于接口实现,Spring AOP默认实现)
  1. 定义计算器接口

    public interface Calculator {
    	int add(int a, int b);
    }
    
  2. 定义计算器接口的实现类

    public class MyCalculatorImpl implements Calculator {
    	@Override
        public int add(int a, int b) {
            return a + b;
        }
    }
    
  3. 定义代理类

    public class CalculatorProxy {
    	public static Object getInstance(final MyCalculatorImpl myCalculator) {
            return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(),
                         myCalculator.getClass().getInterfaces(),
                         new InvocationHandler() {
                         	/**
                         	 * @param proxy 代理对象
                         	 * @param method 代理的方法
                         	 * @param args 方法的参数
                         	 * @return
                         	 * @throws Throwable
                         	 */
                             public Object invoke(Object proxy, Method method, 
                                                  Object[] args) throws Throwable {
                                 System.out.println(method.getName() + "方法执行将要执行...");
                                 Object invoke = method.invoke(myCalculator, args);
                                 System.out.println(method.getName() + "方法执行完毕...");
                                 
                                 return invoke;
                             }
                         });
        }
    }
    

Proxy.newProxyInstance方法的参数:第一个是被代理类的类加载器,第二个是代理对象实现的接口,第三个是代理对象方法的处理器,所有要额外添加的行为都在invoke方法中实现。

2.2.2 cglib动态代理(基于类继承)

代理的类不能为final,否则报错(在内存中构建子类做扩展,被代理类必须被继承)

目标对象的方法如果为final/static,那么不会被拦截

//需要实现的MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor {
    //目标对象
    private Object target;
    
    public ProxyFactory(Object target) {
        this.target = target;
    }
    
    //给目标对象创建代理对象
    public Object getProxyInstance() {
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类代理(代理对象)
        return en.create();
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
        System.out.println("开始事务...");
        //执行目标对象的方法
        //Object returnValue = method.invoke(target, args);
        proxy.invokeSuper(target, args);
        System.out.println("提交事务...");
        
        return returnValue;
    }
}

2.4 AOP编程

2.4.1 配置文件中开启AOP注解方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
 <context:component-scan base-package="aa"/>
 	<!-- 开启aop注解⽅式 -->
 	<aop:aspectj-autoproxy/>
</beans>

使用Java Configuration方式使能@AspectJ

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    
}
2.4.2 定义Aspect(切面类)

使用注解@Aspect注解一个Bean后,Spring框架会自动收集这些Bean,并添加到Spring AOP中

@Component
@Aspect//指定为切面类
public class LogAspect {
    
}
  • 注意, 仅仅使用@Aspect 注解, 并不能将一个 Java 对象转换为 Bean, 因此我们还需要使用类似 @Component 之类的注解。

  • 并且, 如果一个 类被@Aspect 标注, 则这个类就不能是其他 aspect 的 advised object 了, 因为使用 @Aspect 后, 这个类就会被排除在 auto-proxying 机制之外

4.2.3 声明pointcut

一个 pointcut 的声明由两部分组成:

  • 一个方法签名, 包括方法名和相关参数
  • 一个 pointcut 表达式, 用来指定哪些方法执行是我们感兴趣的(即因此可以织入 advice)。
  • pointcut方法必须无返回值,方法本身就是 pointcut signature, pointcut 表达式使用@Pointcut 注解指定

在@AspectJ 风格的 AOP 中, 我们使用一个方法来描述 pointcut

//定义pointcut,该方法必须无返回值,这个方法本身就是 pointcut signature, pointcut 表达式使用@Pointcut 注解指定
/**
 * 统一定义切点
 * 第一个 * 表示要拦截的目标方法返回值为任意
 * 第二个 * 表示包中的任意类
 * 第三个 * 表示类中的任意方法
 * 最后的两个点表示方法参数任意,个数任意,类型任意
 */ 
@Pointcut("execution(* xxx.*.*(..))")//切点表达式
private void pointcut() {} //切点前面

/**
 * @param joinPoint 包含了目标方法的关键信息
 * @Before注解表示这是一个前置通知,在目标方法执行之前执行
 */
//⾥⾯的值为切⼊点表达式
@Before(value = "pointcut()")
public void begin(JoinPoint jionPoint) {
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
	System.out.println(name + "方法开始执行");
}

/**
 * @param joinPoint 包含了目标方法的关键信息
 * @After注解表示这是一个后置通知,在目标方法执行之后执行
 */
@After(value = "pointcut()")
public void close() {
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
	System.out.println(name + "方法执行结束");
}

/**
 * @AfterReturning表示返回通知,即目标方法有返回值时才会触发。
 * 该注解中的returning属性表示目标方法返回值的变量名,需要和参数一一对应
 * 注意:这里方法返回值参数类型要和目标方法返回的类型一致,否则拦截不到
 * 如果想拦截所有方法(包括返回值为void),则方法返回值参数可以为Object
 */
@AfterReturning(value = "pointcut()", returning = "r")
public void returning(JointPoint jointPoint, Integer r) {
    Signature signature = jointPoint.getSignature();
    String name = signature.getName();
    System.out.printlin(name + "方法返回" + r);
}

/**
 * 异常通知
 * @param e 目标方法所抛出的异常,这个参数必须是方法所抛出的异常后置所抛出的异常的父类
 * 如果想拦截所有,参数类型声明为Exception
 */
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
    Signature signature = jointPoint.getSignature();
    String name = signature.getName();
    System.out.printlin(name + "方法抛出异常" + e.getMessage());
}

/**
 * 环绕通知
 * 可用通过环绕通知实现上面四个通知,这个方法的核心在于使用类似于反射的机制执行方法
 * @param pjp
 * @return 注意这里的返回值类型最好是Object,和拦截到的方法相匹配
 */
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) {
    Object proceed = null;
    try {
        //相当于method.invoke方法,可以在这个方法的前后分别添加日志,起到前置/后置通知作用
    	proceed = pjp.proceed();    
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    
    return proceed;
}

常见的切点表达式:

// 匹配指定包中的所有的方法
execution(* com.xys.service.*(..))

// 匹配当前包中的指定类的所有方法
execution(* UserService.*(..))

// 匹配指定包中的所有 public 方法
execution(public * com.xys.service.*(..))

// 匹配指定包中的所有 public 方法, 并且返回值是 int 类型的方法
execution(public int com.xys.service.*(..))

// 匹配指定包中的所有 public 方法, 并且第一个参数是 String, 返回值是 int 类型的方法
execution(public int com.xys.service.*(String name, ..))
4.2.3 测试
public class Main {
	public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        MyCalculatorImpl myCalculator = (MyCalculatorImpl) ctx.getBean("MyCalculatorImpl");
        myCalculator.add(3, 4);
        myCalculator.min(5, 6);
        
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值