您的位置:首页 > 编程语言 > Java开发

【JavaEE学习笔记】Spring_03_IoC的其他配置方式,AOP浅析

2018-01-27 20:33 786 查看
Spring_03

A.IoC的其他配置方式

1.xml+Annotation

bean

package org.wpf.spr_01;

import org.springframework.stereotype.Component;

@Component("aa")
public class Demo {

}

配置类

package org.wpf.spr_01;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) // 这个注解可以用于类上进行声明
@Retention(RetentionPolicy.RUNTIME) // 这个注解会一直保持到运行时
@Documented
public @interface Compentent {
// 参数value是字串类型,默认值为空串;如果一个注解中有且仅有一个属性为value时,可以不写value=
// 这个注解用于声明一个受管bean,其中的value参数为受管bean的id名称
String value() default "";
}

配置文件

<?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"
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"> <bean id="aa" class="org.wpf.spr_01.Demo" />
</beans>

测试类

package org.wpf.spr_01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = ac.getBean("aa", Demo.class);
System.out.println(demo);
}
}

手动配置Spring自动扫描发现受管bean

另外Spring提供的其它注解:
@Controller用于声明一个控制器受管bean

@Service用于声明一个业务受管bean

@Repository用于声明一个DAO类型的受管bean

@Component用于声明一个无法明确区分用途的受管bean

2.基于JavaConfig配置

配置类

package org.wpf.spr_01;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration	// 用于说明当前类是一个配置类
public class DemoConfig {
@Bean // 定义受管bean,方法名称就是受管bean的名称
public Demo aa() {
return new Demo();
}
}

测试类

package org.wpf.spr_01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(DemoConfig.class);
Demo demo = ac.getBean("aa", Demo.class);
System.out.println(demo);
}
}


3.bean之间的关系

继承和依赖

Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean, 继承这个父 Bean 的 Bean 称为子 Bean

子 Bean 从父 Bean 中继承配置, 包括 Bean 的属性配置

子 Bean 也可以覆盖从父 Bean 继承过来的配置

父 Bean 可以作为配置模板, 也可以作为 Bean 实例, 若只想把父 Bean 作为模板, 可以设置 <bean> 的abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean

并不是 <bean> 元素里的所有属性都会被继承. 比如: autowire, abstract 等

也可以忽略父 Bean 的 class 属性,让子 Bean 指定自己的类, 而共享相同的属性配置, 但此时 abstract 必须设为 true

<bean id="now" class="java.util.Date" />
<!-- 没有对应的类定义,只是用于简化继承于它的受管bean的配置 -->
<bean id="myParent" p:birth-ref="now" abstract="true" />
<bean id="aa" class="com.wpf.ioc04.A" parent="myParent" />
<!-- 这里实际上也需要注入birth,但是不需要再次配置定义,因为parent="myParent",表示这个受管bean继承了myParent的配置 -->
<bean id="bb" class="com.wpf.ioc04.B" parent="myParent" />

Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称

默认情况下按照配置的顺序构建受管bean对象

<!-- 设置要求先创建aa然后再创建bb.注意这里并没有将aa对象注入到bb对象中 -->
<bean id="bb" class="com.yan.ioc05.B" depends-on="aa" />
<bean id="aa" class="com.yan.ioc05.A" />

<!-- ------------------------------------------------ -->

<bean id="bb" class="com.yan.ioc05.B" p:a-ref="aa" />
<!-- 注入被依赖的对象可以保证一定会被注入,但是不保证构建aa的顺序 -->
<bean id="aa" class="com.yan.ioc05.A" />

执行顺序为:B.B()    A.A()    B.setA()

4.使用外部属性文件和spEL表达式

在配置文件里配置 Bean 时,有时需要在 Bean 的配置里混入系统部署的细节信息

分离配置的思想(例如文件路径, 数据源配置信息等)

而这些部署细节实际上需要和 Bean 配置相分离

在src目录下创建properties文件,这里包含数据库连接的相关配置信息

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=123456

配置文件

<!-- 使用context名空间可以实现读取并解析特定的资源文件,并在其它配置的地方通过SpEL【Spring表达式语言】进行调用,以简化配置信息 -->
<context:property-placeholder location="classpath:database.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${jdbc.driver}" p:jdbcUrl="${jdbc.url}" p:user="${jdbc.username}"
p:password="${jdbc.password}" destroy-method="close" />
<!-- 在xml配置中只管理连接,不包含数据库相关配置信息 -->
<bean id="aa" class="com.yan.ioc06.A" p:driver="${jdbc.driver}"
p:ds-ref="dataSource" />

spEl

Spring 表达式语言,简称SpEL:是一个支持运行时查询和操作对象图的强大的表达式语言。

语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL 

SpEL 为 bean 的属性进行动态赋值提供了便利

B.AOP浅析(了解)

1.引入

业务需求:完成用户功能的处理代码

系统需求:日志,安全,事务等

如果把系统需求加入到处理业务需求的类中,那么业务类就会变得很复杂和零乱

甚至可能会喧宾夺主,而且以后要变更系统需求的时候,将给软件带来很大的维护量

代码混乱:越来越多的非业务需求(日志和验证等)加入后, 原有的业务方法急剧膨胀.  每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点.

代码分散: 以日志需求为例, 只是为了满足这个单一需求, 就不得不在多个模块(方法)里多次重复相同的日志代码. 如果日志需求发生变化, 必须修改所有模块

底层原理是代理模式

2.分散关注思路

将通用需求功能从不相关类之中分离出来

能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以

AOP就是这种实现分散关注的编程方法,它将“关注”封装在“方面aspect”中

3.AOP

AOP即Aspect-Oriented Programming,面向切面编程:是一种新的方法论,是一种技术,不是设计模式,是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充

AOP 的主要编程对象是切面(aspect),而切面模块化横切关注点

在应用 AOP 编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就被模块化到特殊的对象(切面)里

AOP 的好处

每个事物逻辑位于一个位置,代码不分散,便于维护和升级

业务模块更简洁,只包含核心业务代码

4.AOP 与IoC的联系与区别

相同点:都是寻求调用者与被调用者的解耦

不同点:

IoC关注的是对象的创建、维护职责与对象的使用权(该使用权与调用者的功能紧密相关)解耦

AOP关注的是功能本身的解耦

5.Spring AOP的优点

实现了分散关注,将通用需求功能从不相关类之中分离出来;

同时,能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以

允许开发者使用声明式企业服务,比如事务服务、安全性服务

开发者可以开发满足业务需求的自定义方面

开发Spring AOP Advice 很方便,可以借助代理类快速搭建Spring AOP 应用

6.AOP术语

连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位

例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置

通知(Advice):  在连接点需要追加的程序处理

切入点pointcut:连接点的对象化表示,比如Filter的<url-pattern>

切面(Aspect):  切入点和通知定义在一个类中,这个类就是切面类

目标(Target): 被调用的程序

代理(Proxy): 向目标对象应用通知之后创建的对象

7.Spring的通知类型

Spring通知类型按切面功能调用的不同时刻,可以分为提供了5种Advice类型

前置通知Before advice:在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)

后置通知After returning advice:在某连接点正常完成后执行的通知

异常通知After throwing advice:在方法抛出异常退出时执行的通知

最终通知After (finally) advice:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)

环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行

8.通知类型的选择

环绕通知是最常用的通知类型

推荐使用尽可能简单的通知类型来实现需要的功能

如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知

用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误

9.SpringAOP的原理

Spring框架中的AOP拦截技术是POJO的方法层面的拦截【拦截的颗粒度较粗】

其底层实现原理是动态代理技术

对于面向接口的方法拦截,依赖于jdk的动态代理技术,即java.lang.reflect.Proxy#newProxyInstance,将对被代理的目标对象的调用,委托到代理对象,触发拦截通知;而当被拦截的方法, 不是在接口中定义时,使用的是cglib,对字节码进行动态增强,生成被代理类的子对象,以实现代理

spring实现aop,动态代理技术的两种实现是jdk动态代理、cglib代理,根据被通知的方法是否为接口方法,来选择使用哪种代理生成策略



10.前置通知

目标接口,注意JDK动态代理要求必须有对应的接口

否则不能使用 JDK动态代理,只能使用CGLIb动态代理

package org.wpf.spr_01;

public interface IHelloServ {
void sayHello();
}

目标实现类---具体的业务逻辑处理类

package org.wpf.spr_01;

public class HelloServImpl implements IHelloServ {

@Override
public void sayHello() {
System.out.println("业务逻辑处理开始........");
System.out.println("hello");
System.out.println("业务逻辑处理结束........");
}

}

定义前置通知类

package org.wpf.spr_01;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

// 这是一个前置通知类。定义通知常见的方法有2种:
// 方法1:实现特定的接口进行定义,但是Spring2+版本中没有最终通知,实现接口定义会导致侵入性,不推荐使用
// 方法2:添加注解进行定义
public class BeforeHelloAdvice implements MethodBeforeAdvice {

@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("前置通知开始。。。。。。");
System.out.println("调用的方法为:" + arg0.getName());
if (arg1 != null && arg1.length > 0)
for (Object temp : arg1)
System.out.println("\t\t调用的参数为:" + temp);
System.out.println("调用的目标对象为" + arg2);
System.out.println("前置通知结束。。。。。。");
}

}

通过使用IoC将通知和业务处理进行组装---代理

<?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"
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"> 
<!-- 定义目标对象 -->
<bean id="hello" class="org.wpf.spr_01.HelloServImpl" />

<!-- 定义通知对象 -->
<bean id="bef" class="com.wpf.spr_01.BeforeHelloAdvice" />

<!-- 定义代理对象,Spring的AOP是依赖于代理将通知织入到目标程序 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 设置具体的目标对象 -->
<property name="target" ref="hello" />
<!-- 定义代理的接口 -->
<property name="interfaces">
<value>org.wpf.spr_01.IHelloServ</value>
</property>
<!-- 定义拦截对象的名称,系统根据事先的接口进行区分不同的执行时机 -->
<property name="interceptorNames">
<value>bef</value>
</property>
</beans>

测试调用

package org.wpf.spr_01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext("applicationContext.xml");
// 获取的不是具体的业务逻辑处理对象,而是通过Spring提供的代理工厂生成的代理对象
IHelloServ hello = ac.getBean("proxy", IHelloServ.class);
hello.sayHello();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring aop IoC