第一天:
Spring是什么:
Spring是一个开源框架,为了解决企业应用开发的复杂性创建的。现在已经不止应用于企业应用
是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架
从大小与开销两方面而言Spring都是轻量的
通过控制反转(IOC)的技术达到松耦合的目的
提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
包含并管理应用对象的配置和生命周期,这个意义上是一种容器
将简单的组件配置,组装成复杂的应用,这个意义上是框架
Spring作用
容器
提供了对多种技术的支持
JMS
MQ支持
UnitTest
.....
AOP(事务管理,日志等)
提供了众多方便应用的辅助类(JDBC Template等)
对主流的应用框架(Hibernate等)提供了良好的支持
适用范围
构建企业应用(SpringMVC + Spring + Hibernate / ibatis)
单独使用Bean容器(Bean管理)
单独使用AOP进行切面处理
其他的Spring功能,如:对消息的支持等
在互联网中的应用............
框架的特点:
半成品
封装了特定的处理流程和控制逻辑
成熟的,不断改进的软件
框架和类库的区别
框架一般是封装了逻辑,高内聚的,类库则是松散的工具组合
框架专注于某一个领域,类库则是通用的
专题一 IOC
接口及面向接口编程
接口:
实体把自己提供给外界的一种抽象化说明,用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式。
对应java接口即声明,声明了哪些方法是对外公开提供的。
在java8中,接口可以拥有方法体。
面向接口编程:
结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类
接口实现的变动不影响各层间的调用。
“面向接口编程” 中的 “接口” 是用于隐藏具体实现和实现多态性的组件
什么是IOC
IOC:控制反转(获得依赖对象的过程被反转了),控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护
DI(依赖注入)是IOC一种实现方式
目的:创建对象并且组装对象之间的关系
在IOC容器中所有的对象都称为Bean
Spring的Bean配置
Spring使用有两种方式,注解或xml配置(这里使用配置)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<!-- id对bean的唯一标识(接口),class是bean对应的类(实现类)。 -->
<bean id="oneInterface" class="com.wk.ioc.interfaces.OneInterfaceImpl"></bean>
</beans>
测试方法:
OneInterface oneInterface = super.getBean("oneInterface");
oneInterface.say("This is a test.");
Bean的初始化
基础:两个包
org.springframework.beans
org.springframework.context
BeanFactory提供配结构和基本功能,加载初始化Bean
ApplicationContext保存了Bean对象并在Spring中被广泛使用
方式,ApplicationContext
本地文件
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("E:/workspace/appcontext.xml");
Classpath
ClassSystemXmlApplicationContext context = new ClassSystemXmlApplicationContext("classpath:spring-context.xml");
Web应用中依赖servlet或Listener
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Spring的常用注入方式
Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为
常用的两种注入方式:
设值注入(通过set方法设值)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="injectionService" class="com.wk.ioc.injection.service.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
<bean id="injectionDAO" class="com.wk.ioc.injection.dao.InjectionDAOImpl"></bean>
</beans>
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
构造注入(通过构造函数设值)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="injectionService" class="com.wk.ioc.injection.service.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.wk.ioc.injection.dao.InjectionDAOImpl"></bean>
</beans>
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
第二天:
专题二 Bean
Bean配置项
Id 在整个IOC容器中Bean的唯一标识
Class 实例化的类(必须)
Scope 作用域
Constructor arguments 构造器的参数
Properties 属性
Autowiring mode 自动装配模式
lazy-initialization mode 懒加载模式
Initialization/destruction method 初始化和销毁方法
Bean的作用域
singleton : 单例,指一个Bean容器中只存在一份(默认)
prototype:每次请求(每次使用)创建新的实例,destroy方法不生效
request : 每次http请求创建一个实例且仅在当前request内有效
session :每次http请求创建,当前session内有效
global session : 基于portlet的web中有效(protlet定义了globalsession),如果是在web中,同session
Bean的生命周期
定义:在xml配置文件中的标签 id class对Bean的定义
初始化:当IOC容器在启动时,加载Bean配置文件中的Bean并初始化,也就是生成Bean的实例
实现 org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法
public class ExampleInitializingBean implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
}
配置init-method (配置完成后,在对应的Bean中一定要有对应的方法,否则会报错)
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean{
public void init(){
//do some initialization work
}
}
销毁:在Bean容器停止是销毁实例
实现org.springframework.beans.factory.DisposableBean接口,覆盖destroy方法
public class ExampleDisposableBean implements DisposableBean{
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
}
}
配置destroy-method
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup" />
public class ExampleBean {
public void cleanup() {
//do some destruction work (like releasing pooled connections)
}
}
配置全局默认初始化,销毁方法(配置全局默认的初始化,销毁方法后,bean中也可以没有这些方法)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-init-method="init" default-destroy-method="destroy">
</beans>
注意:
如果同时使用了三种方法初始化和销毁,则先执行实现接口的方法,再执行配置的init-method / destroy-method方法,配置的全局默认初始化,销毁方法不执行。
Aware
Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在初始化之后,可以获得相应的资源
通过Aware接口,可以对Spring相应资源进行操作
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
//BeanNameAware :提供BeanName定义的内容
//ApplicationContextAware :提供上下文信息
public class BeanName implements BeanNameAware, ApplicationContextAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("BeanName : " + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
System.out.println("setApplicationContext : " + applicationContext.getBean(this.beanName).hashCode());
}
}
Bean的自动装配(Autowiring):
No:不做任何操作( 默认选项 )
byname:根据属性名自动装配。此选项将检查IOC容器并根据名字查找与属性完全一致的bean,并将其属性自动装配( 根据id查找 )
spring-autowiring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byname">
<!-- id名称要和class名称对应 -->
<bean id="autoWiringService" class="com.wk.autowiring.AutoWiringService" ></bean>
<!-- id名称要和class名称对应 -->
<bean id="autoWiringDAO" class="com.wk.autowiring.AutoWiringDAO" ></bean>
</beans>
对应的Bean中通过set方法注入
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) {
System.out.println("setAutoWiringDAO");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String word) {
this.autoWiringDAO.say(word);
}
}
byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型的bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么都不发生
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="autoWiringService" class="com.wk.autowiring.AutoWiringService" ></bean>
<!-- 通过类型就是根据class,这里的id可以随便取或不取 -->
<bean class="com.wk.autowiring.AutoWiringDAO" ></bean>
</beans>
对应的Bean中通过set方法注入(同上)
Constructor:与byType方式类似根据属性类型相同的bean,不同之处在于它应用于构造参数。如果容器中没有找到于构造器参数一致的bean,那么抛出异常
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<bean id="autoWiringService" class="com.wk.autowiring.AutoWiringService" ></bean>
<!-- 和byType方法类似,也是根据属性类型相同的bean查找,所以这里的id的也可以随意取名,或不取 -->
<bean class="com.wk.autowiring.AutoWiringDAO" ></bean>
</beans>
对应的Bean中通过构造器注入
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public AutoWiringService(AutoWiringDAO autoWiringDAO) {
System.out.println("AutoWiringService");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String word) {
this.autoWiringDAO.say(word);
}
}
Resources&ResourceLoader
Resources:
spring中针对资源文件的统一接口
Resources类型:
-UrlResource:URL对应的资源,根据一个URL地址即可构建
-ClassPathResource:获取类路径下的资源文件
-FileSystemResource:获取文件系统里的资源
-ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源
-InputStreamResource::针对输入流封装的资源
-ByteArrayResource:针对于字节数组封装的资源
ResourceLoader:
是对Resource加载的一个接口,在Spring的IOC容器中所有的application contexts都实现了ResourceLoader接口,所有的application contexts都可以用来获得Resource的实例
ResourceLoader接口的声明:
public interface ResourceLoader{
Resource getResource(String location);
}
例:
Resource template = ctx.getResource("path/myTemplate.txt");
Resource template = ctx.getResource("classpath:path/myTemplate.txt");
Resource template = ctx.getResource("file:/path/myTemplate.txt");
参数:
前缀 | 例子 | 说明 |
classpath: | classpath:path/myTemplate.txt | 从classpath中加载 |
file: | file:/path/myTemplate.txt | 加载文件系统路径 |
http: | http://myserver/logo.png | 作为URL加载 |
(none) | /data/config.xml | 依赖潜在的ApplicationContext |
第三天:
Bean的定义及作用域的注解实现
Bean管理的注解实现及例子
Classpath扫描与组件管理
- 从Spring3.0开始 , Spring JavaConfig项目提供了很多特性,包括使用java而不是XML定义bean,比如 @Configuration , @Bean , @Import , @DependsOn
- @Component是一个通用注解,可用于任何bean
- @Repository 通常用于注解DAO类,即持久层
- @Service 通常用于注解Service类,即服务层
- @Controller 通常用于Controller类,即控制层(MVC)
元注解(Meta-annotations)
- 许多Spring提供的注解可以作为自己的代码,即 " 元数据注解 " , 元注解是一个简单的注解,可以应用到另一个注解(即注解的注解)
- 除了value( ),元注解还可以有其它的属性,允许定制
类的自动检测及Bean的注册
- Spring 可以自动检测类并注册Bean到ApplicationContext中
- 为了能够检测这些类并注册相应的Bean,需要下面的内容
<?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: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" >
<!-- 扫描org.example包下基于类的注解 -->
<context:component-scan base-package="org.example"/>
</beans>
- < context : component-scan >(扫描所有注解)包含< context : annotation-config />(只能扫描类中的注解)通常在使用前者后,不用再使用后者(在使用前者后已经包含了后者的全部功能)
- AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来
< context : annotation-config />
- 通过在基于XML的Spring配置如下标签
<?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: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" >
<context:annotation-config/>
</beans>
- < context : annotation-config / >仅会查找在同一个applicationContext中的Bean注解
使用过滤器进行自定义扫描
- @默认情况下,类被自动发现并注册bean的条件是:使用@Component , @Repository , @Service , @Controller注解或者使用@Component的自定义注解
- @可以通过过滤器修改上面的行为,如:下面例子的XML配置忽略所有的@Repository注解并用“Stub”代替
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
- @还可使用use-default-filters = "false" 禁用自动发现与注册
过滤类型 | 示例 |
annotation | org.example.SomeAnnotation |
assignable | org.example.SomeClass |
aspectj | org.example..*Service+ |
regex | org\.example\.Default.* |
custom | org.example.MyTypeFilter |
定义Bean
- 扫描过程中组件被自动检测,那么Bean的名称由BeanNameGenerator生成的(@Component , @Repository , @Service , @Controller都会有个name属性用于显式设置Bean Name)
设置BeanName为 myMovieLister
@Service("myMovieLister")
public class SimpleMovieLister{
// ...
}
设置BeanName为 movieFinderImpl
@Repository
public class MovieFinderImpl implements Movie{
// ...
}
- 可自定义bean的命名策略,实现BeanNameGenerator接口,并一定要包含一个无参的构造函数
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
作用域(Scope)
- 通常情况下自动查找的Spring组件,其scope是singleton,从Spring2.5开始提供了一个标识scope的注解@Scope
@Scope("prototype")
@Repository
public class MovieFinderImpl implements Movie{
// ...
}
- 也可以自定义scope策略,实现ScopeMetadataResolver接口并提供一个无参构造器
<beans>
<context:component-scan base-package="org.example"
scope-resolver="org.xample.MyNameGenerator" />
</beans>
代理方式
- 可以使用scoped-proxy属性指定代理,有三个值可选:no , interfaces , targetClass
<beans>
<context:component-scan base-package="org.example"
name-proxy="interfaces" />
</beans>
@Required
- @required注解适用于bean属性的setter方法
- 这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
// ...
}
@Autowired
- 可以将@Autowired注解为 "传统" 的setter方法
- 可以用于构造器或成员变量
@Service
public class InjectionServiceImpl implements InjectionService {
//三种注入方式
// @Autowired
private InjectionDAO injectionDAO;
@Autowired
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
// @Autowired
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
public void save(String arg) {
// 模拟业务操作
System.out.println("Service接收参数:" + arg);
arg = arg + ":" + this.hashCode();
injectionDAO.save(arg);
}
}
- 默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过下面的方式避免
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
// ...
}
- 每一个类只能有一个构造器被标记为required = true
- @Autowired的必要属性,一般使用@Required注解
- 可以使用@Autowired注解那些解析性依赖接口,如:BeanFactory , ApplicationContext , Environment , ResourceLoader , ApplicationEventPublisher , MessageSource
public class ppt{
//获得上下文信息
@Autowired
private ApplicationContext context;
// ...
}
- 可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中的所有特定类型的bean
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs){
this.movieCatalogs = movieCatalogs;
}
- 可以用于装配key为String的Map
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs){
this.movieCatalogs = movieCatalogs;
}
- 如果希望数组有序,可以让bean实现org.springframework.core.Oredred接口或使用的@Order注解
- @Autowired是由Spring BeanPostProcessor处理的,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解,这些类型必须通过XML或者Spring的@Bean注解加载
@Qualifier
- 按类型自动装配可能多个bean实例的情况,可以使用Spring的@Qualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数
- 可用于注解集合类型变量
public class MovieRecommender{
//指定用main来创建类赋值声明的接口
//@Autowired
//@Qualifier("main")
private MovieCatalog movieCatalog;
@Autowired
public void prepare(@Qualifier("main")MovieCatalog movieCatalog){
this.movieCatalog = movieCatalog;
}
// ...
}
- 在xml文件中配置的方式是:
<?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: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" >
<context:annotation-config/>
<bean class= "example.SimpleMovieCatalog">
<qualifier value="main"/>
</bean>
<bean class= "example.SimpleMovieCatalog">
<qualifier value="action"/>
</bean>
<bean id="movieRecommender" class="example.MovieRecommender">
</beans>
- 如果通过名字进行注解注入,主要使用的不是@Autowired( 即使在技术上能够通过@Qualifier指定bean的名字 ),替代方式是使用JSR-250@Resource注解,它是通过其独特的名称来定义来识别特定的目标(这是一个与所声明的类型是无关的匹配过程)
- 因语义差异,集合或Map类型的bean无法通过@Autowired来注入,因为没有类型匹配到这样的bean,为这些bean使用@Resource注解,通过唯一名称引用集合或Map的bean
- @Autowired适用于fields , constructors , multi-argumentmethods这些允许在参数级别使用@Qualifier注解缩小范围的情况
- @Resource适用于成员变量,只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式是使用qualifiers
- 自己定义qualifier注解并使用
@Target((ElementType.FIELD, ElementType.PARAMETER))
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre{
String value();
}
public class MovieRecommender{
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
// ...
}
- 使用xml定义qualifier注解
<?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: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" >
<context:annotation-config/>
<bean class= "example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
</bean>
<bean id="movieRecommender" class="example.MovieRecommender">
</beans>
基于java的容器注解
- @Bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件的<bean/>
- 可以在Spring的@Component注解的类中使用@Bean注解任何方法(在方法里创建对象并返回)(仅仅是可以,通常使用的是@Configuration)
@Configuration
public class AppConfig{
//Bean name是"myService"
@Bean
public MyService myService(){
return new MyServiceImpl();
}
}
上面的代码相当于在xml中做了如下配置
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
- 自定义Bean name
@Configuration
public class AppConfig{
@Bean(name = "myFoo")
public Foo foo(){
return new Foo();
}
}
- init-method
- destroy-method
public class Foo{
public void init(){
// initialization logic
}
}
public class Bar{
public void cleanump(){
// destruction logic
}
}
@Configuration
public class AppConfig{
@Bean(initialization = "init")
public Foo foo(){
return new Foo();
}
@Bean(destroyMethod = "cleanup")
public Bar bar(){
return new Bar();
}
}
使用@ImportResource和@Value注解进行资源文件读取
使用XML文件中的配置示例:
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-cofig/>
<!-- 加载资源文件,资源文件中的jdbc.url等的值都是键值对的形式存在的 -->
<context:property-placeholder location="classpath:/com/acme/jdbc.properties">
<bean class="com.acme.AppConfig"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
使用注解配置的示例:
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig{
//从资源文件中取出jdbc.url的值并赋值给url
@Value("$(jdbc.url)")
private String url;
@Value("$(jdbc.username)")
private String username;
@Value("$(jdbc.password)")
private String password;
@Bean
public DataSource dataSource(){
return new DriverManagerDataSource(url,username,password);
}
}
@Bean和@Scope
- 默认@Bean是单例的
@Configuration
public class MyConfiguration{
//Bean的作用域包括singleton, prototype, request, session, global session
@Bean
@Scope("prototype")
public Encryptor encryptor(){
// ...
}
}
@Bean
@Scope(value = "session",proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences(){
return new UserPreferences();
}
@Bean
public Service userService(){
UserService service = new SimpleUserService();
// a reference to the proxied userPreferences bean
service.setUserPreferences(userPreferences);
return service;
}
基于泛型的自动装配(Spring4中新增的内容)
@Configuration
public class MyConfiguration{
@Bean
public StringStore stringStore(){
return new StringStore();
}
@Bean
pulbic IntegerStore integerStore(){
return new IntegerStore();
}
}
@Autowired
private Store<String> s1;// <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2;// <Integer> qualifier, injects the integerStore bean
//Inject all Store beans as long as they have an <Integer> generic
//Store<String> beans will not appear in this list
@Autowiredprivate List<Store<Integer>> s;
自定义qualifier注解
- CustomAutowireConfigurer是BeanFactoryPostProcessor的子类,通过它可以注册自己的qualifier注解类型(即使没有使用Spring的@Qualifier注解)
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
- 该AutowireCandidateResolve决定自动装配的候选者:
* 每个Bean定义的autowire-candidate值
* 任何<bean/>中的default-autowire-candidates
* @Qualifier注解及使用CustomAutowireConfigurer的自定义类型
Spring对JSR支持的说明:
@Resource
- Spring支持使用JSR-250@Resource注解的变量或setter方法,这是一种在Java EE 5和6的通用模式,Spring管理的对象也支持这种模式
- @Resource有一个name属性,并且默认Spring解释该值作为被注入bean的名称
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
}
- 如果没有显式的指定@Resource的name,默认的名称从属性名(注解在属性上)或者setter方法(注解在setter方法上)得出
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
}
- 注解提供的名字被解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的
@PostConstruct 和 @PreDestroy
- CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解@Resource,在Spring2.5中引入支持初始化回调和销毁回调,前提是CommonAnnotationBeanPostProcessor是Spring的ApplicationContext中注册的
public class CachingMovieLister{
//在初始化之前执行,相当于初始化方法
@PostConstruct
public void populateMovieCache(){
// populates the movie cache upon initialization...
}
//在销毁之前执行,相当于销毁方法
@preDestroy
public void clearMovieCache(){
// clears the movie cache upon destruction...
}
}
使用JSR330标准注解
- 从Spring3.0开始支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致
- 使用JSR330需要依赖javax.inject包
- 使用Maven引入方式
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Inject
- @Inject等效于@Autowired,可以使用于类,属性,方法,构造器
import javax.inject.Inject;
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
// ...
}
@Named
- 如果想使用特定名称进行依赖注入(有多个实现类,指定其中的一个),使用@Named
- @Named与@Component也是等效的
import javax.inject.Inject;
import javax.inject.Named;
@Named("movieListener")
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
// ...
}
第四天
专题三 AOP
什么是AOP
- AOP : Aspect Oriented Programming的缩写,是面向切面编程(各模块之间相同的功能公用相同的代码),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
- 主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等
AOP实现方式
- 预编译 AspectJ
- 运行期动态代理(JDK动态代理,CGLib动态代理) SpringAOP , JbossAOP
AOP相关概念
名称 | 说明 |
切面(Aspect) | 一个关注点的模块化,这个关注点可能会横切多个对象 |
连接点(Joinpoint) | 程序执行过程中的某个特定的点 |
通知(Advice) | 在切面的某个特定的连接点上执行的动作 |
切入点(Pointcut) | 匹配连接点的断言,在AOP中通知和一个切入点表达式关联 |
引入(Introduction) | 在不修改类代码的前提下,为类添加新的方法和属性 |
目标对象(Target Object) | 被一个或多个切面所通知的对象 |
AOP代理(AOP Proxy) | AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行动功能) |
织入(Weaving) | 把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入,类加载时织入,执行时织入 |
Advice的类型(Advice:在切面特定连接点执行的动作)
名称 | 说明 |
前置通知(Before advice) | 在某连接点(join point)之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常) |
返回后通知(After returning advice) | 在某连接点(join point)正常完成后执行通知 |
抛出异常后通知(After throwing advice) | 在方法抛出异常退出时执行的通知 |
后通知(After(finally)advice) | 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出) |
环绕通知(Around Advice) | 包围一个连接点(join point)的通知 |
Spring框架中AOP的用途
- 提供了声明式的企业服务,特别是EJB(企业级java Bean)的替代服务的声明
- 允许用户定制自己的切面,以完成OOP(面向对象编程)与AOP(面向切面编程)的互补使用
Spring的AOP实现
- 纯java实现,无需特殊的编译过程,不需要控制类加载器层次
- 目前只支持方法执行连接点(通知Spring Bean的方法执行)
- 不是为了提供最完整的AOP实现;而是侧重于提供一种AOP实现的Spring IoC容器之间的整合,用于帮助解决企业应用中的常见问题
- Spring AOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案
有接口和无接口的Spring AOP实现区别
- Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理
- Spring AOP中也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)
配置切面aspect(基于Schema-based的AOP实现)
- Spring所有的切面和通知器都必须放在一个<aop:config>内(可以配置包含多个<aop:config>元素),每一个<aop:config>可以包含pointcut , advisor 和 aspect元素(它们必须按照顺序进行相应的声明)
- <aop:config>风格的配置大量使用了Spring的自动代理机制
- aspect
* <aop:config>
* <aop:aspect>
<aop:config>
<!-- 把aBean这个Bean当做切面 -->
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
<bean id="aBean" class="...">
...
</bean>
</aop:config>
配置切入点Pointcut(在AOP中通过Advice和一个切入点表达式关联)
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
exression="execution(* com.xyz.myapp.service..(..))"/>
...
</aop:aspect>
</aop:config>
advice
- Before advice
<aop:aspect id="beforeExample" ref="aBean">
<!-- 指定切入点引用 -->
<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
<aop:aspect id="beforeExample" ref="aBean">
<!-- 直接指定切入点,调用aBean中的doAccessCheck方法执行 -->
<aop:before
pointcut="execution(* com.xyz.myapp.dao..(..))"
method="doAccessCheck"/>
...
</aop:aspect>
例子:
配置文件
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="aspect" class="com.wk.aop.schema.advice.Aspect"></bean>
<bean id="aspectBiz" class="com.wk.aop.schema.advice.biz.AspectBiz"></bean>
<aop:config>
<aop:aspect id="AspectAOP" ref="aspect">
<!-- 切入点表达式会切入到陪陪的包和方法上去 -->
<aop:pointcut expression="execution(* com.wk.aop.schema.advice.biz.*Biz.*(..))" id="wkPiontcut"/>
<!-- 方法before在id为aspect的bean中 -->
<aop:before method="before" pointcut-ref="wkPiontcut"/>
</aop:aspect>
</aop:config>
</beans>
Aspect类
package com.wk.aop.schema.advice;
import org.aspectj.lang.ProceedingJoinPoint;
//Aspect类为切面类
public class Aspect {
public void before() {
System.out.println("Aspect before.");
}
}
AspectBiz类
package com.wk.aop.schema.advice.biz;
//业务类
public class AspectBiz {
public void biz() {
System.out.println("AspectBiz biz.");
}
}
测试
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPSchemaAdvice extends UnitTestBase {
public TestAOPSchemaAdvice() {
super("classpath:spring-aop-schema-advice.xml");
}
@Test
public void testBiz() {
AspectBiz biz = super.getBean("aspectBiz");
biz.biz();
}
}
输出运行结果:
Aspect before.
AspectBiz biz.
- After returning advice
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
</aop:aspect>
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
returning="retVal"
method="doAccessCheck"/>
</aop:aspect>
- After throwing advice
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
method="doRecoveryActions"/>
...
</aop:aspect>
- 使用throwing属性来指定可被传递的异常的参数名称
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
throwing="dataAccessEx"
method="doRecoveryActions"/>
...
</aop:aspect>
- After(finally)advice
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop:after
pointcut-ref="dataAccessOperation"
method="doReleaseLock"/>
...
</aop:aspect>
- Around advice
- 通知方法的第一个参数必须是ProceedingJoinPoint类型
<aop:aspect id="aroundExample" ref="aBean">
<aop:around
pointcut-ref="businessService"
method="doBasicProfiling"/>
...
</aop:aspect>
pulbic Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatchre
return retVal;
}
- Advice parameters
public interface FooService{
Foo getFoo(String fooName,int age);
}
public class DefaultFooService implements FooService{
public Foo getFoo(String name,int age){
return new Foo(name,age);
}
}
public class SimpleProfiler{
//环绕通知指定的方法
public Object profile(ProceedingJoinPoint call,String name, int age) throws Throwable{
StopWatch clock = new StopWatch("Profiling for" + name +" and "+ age +"");
try{
clock.start(call.toShortString());
return call.proceed();
}finally{
clock.Stop();
System.out.println(clock.prettyPrint());
}
}
}
相关配置
<!-- this is the object that will be proxied by Spring's AOP infrastructure -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- this is the actual advice itself -->
<bean id="profiler" class="x.y.SimpleProfiler">
<aop:config>
<aop:aspect ref="profiler">
<aop:pointcut id="theExecutionOfSomeFooServiceMethod"
expression="execution(*x.y.service.FooService.getFoo(String,int))
and args(name,age)"/>
<aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"
method="profile"/>
</aop:aspect>
</aop:config>
Introductions
- 简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表在这些对象
- 由<aop:aspect>中的<aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent
<aop:aspect id="usageTrackerAspect" ref="usageTracking">
<!-- implement-interface指定一个接口,default-impl指定对应接口的实现类 -->
<!-- types-methching匹配到的所有类,都用指定的接口,和实现的类代替。用getBean获得匹配相符的类。 -->
<aop:declare-parents
types-metching="com.xzy.myapp.service.*+"
implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>
<aop:before
pointcut="com.xyz.myapp.SystemArchitecture.businessService()
and this(usageTracked)"
method="recordUsage"/>
</aop:aspect>
public void recordUsage(UsageTracked usageTracked){
usageTracked.incrementUseCount();
}
UsageTracked usageTracked = (UsageTracked) context.getBean("myService");
- schema-defind aspects(基于配置文件的)只支持singleton model
Advisors
- advisor就像一个小的自包含方面,只有一个advice
- 切面自身通过一个bean表示,并且必须实现某个advice接口,同时advice也可以很好的利用AspectJ切入点表达式
- Spring通过配置文件中<aop:advisor>元素支持advisor实际使用中,大多数情况下它会和transactional advice配合使用
- 为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service..(..))"/>
<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice">
<tx:attricutes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
Spring AOP API
- 这是Spring1.2历史用法,现在版本任然支持
- 这是SpringAOP基础,应该了解
- 现在用法比历史用法更简便了
Pointcut
- 实现之一:NameMatchMethodPointcut,根据方法名字进行匹配
- 成员变量:mappedNames,匹配的方法名集合
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
Before advice
- 一个简单的通知类型
- 只是在进入方法之前被调用,不需要MethodInvocation对象
- 前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值
public interface MethodBeforeAdvice extends BeforeAdvice{
void before(Method m,Object[] args,Object target) throws Throwable;
}
public class CountingBeforeAdvice implements MethodBeforeAdvice{
private int count;
public void before(Method m,Object[] args,Object target)throws Throwable{
++count;
}
public int getCount(){
return count;
}
}
Throws advice
- 如果连接点抛出异常,throws advice在连接点放回后被调用
- 如果throws-advice的方法抛出异常,那么它将覆盖原有异常
- 接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法。
void afterThrowing([Method,args,target],throwableSubclass);
例:
public static class CombinedThrowsAdvice implements ThrowsAdvice{
public void afterThrowing(RemoteException ex) throws Throwable{
// Do something with remote exception
}
public void afterThrowing(Method m,Object[] args,Object target,ServletException ex){
// Do something with all arguments
}
}
After Returning advice
- 后置通知必须实现org.springframework.aop.AfterReturningAdvice接口
public class CountingAfterReturningAdvice implements AfterReturningAdvice{
private int count;
public void afterReturning(Object returnValue,Method m, Object[] args,Object target)throws Throwable{
++count;
}
public int getCount(){
return count;
}
}
- 可以访问返回值(但不能进行修改),被调用的方法,方法的参数和目标
- 如果抛出异常,将会抛出拦截器链,替代返回值
Interception around advice
- Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点
public interface MethodInterceptor extend Interceptor{
Object invoke(MethodInvocation invocation)throws Throwable;
}
pulbic class DebugInterceptor implements MethodInterceptor{
public Object invoke(MethodInvocation invocation)throws Throwable{
System.out.println("Before:invocation=["+invocation+"]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
Introduction advice
- Spring把引入通知作为一种特殊的拦截通知
- 需要IntroductionAdvisor和IntroductionInterceptor
- 仅适用于类,不能和任何切入点一起使用
public interface IntroductionInterceptor extends MethodInterceptor{
boolean implementsInterface(Class intf);
}
public interface IntroductionAdvisor extends Advisor,IntroductionInfo{
ClassFilter getClassFilter();
void validateInterfaces()throws IllegalArgumentException;
}
public interface IntroductionInfo{
Class[] getInterfaces();
}
- introduction advisor比较简单,持有独立的LockMixin实例
public class LockMixinAdvisor extends DefaultIntroductionAdvisor{
public LockMixinAdvisor(){
super(new LockMixin(),Lockable.class);
}
}
Advisor API in Spring
- Advisor是仅包含一个切入点表达式关联的单个通知的方面
- 除了introduction是,advisor可以用于任何通知
- org.springframework.aop.support.DefaultPointcutAdvisor是最常用的advisor类,它可以于MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用
- 它可以混合在Spring同一个AOP代码的advisor和advice
ProxyFactoryBean
- 创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
- 这可以完全控制切入点和通知(advice)以及他们的顺序
(例如:定义了一个Bean的id为foo的ProxyFactoryBean实例。引用foo这个对象得到的不是ProxyFactoryBean实例,而是
ProxyFactoryBean实现里getObject()方法创建的对象。getObject方法将创建一个AOP代理包装一个目标对象)
- 使用ProxyFactoryBean或者其它Ioc相关类来创建AOP代理,通知和切入点也可以由Ioc来管理
- 被代理类没有实现任何接口,使用CGLIB代理,否则JDK代理
- 通过设置proxyTargetClass为true,可强制使用CGLIB
- 如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean配置
- 如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于JDK的代理将被创建
- 如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者多个)
接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理
Proxying class
- 没有使用接口的情况下,Spring会使用CGLIB代理,而不是JDK代理
- CGLIB代理的工作远离是在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标
- 子类是用来实现Decorator模式,织入通知
- CGLIB的代理对用户是透明的,
* final方法不能被通知,因为它们不能被覆盖
* 不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心的JAR(即基于
CGLIB的AOP就像JDK动态代理一样)
使用global advisors
- 用*做通配,匹配所有拦截器加入通知群
<bena id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"/>
<property name="interceptorNames">
<list>
<value>global*</value>
</list>
</bean>
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
简化的proxy定义
- 使用父子bean定义,以及内部bean定义,可能会带来更清洁和更简洁的代理定义(抽象属性标记父bean定义为抽象的这样它不能被实例化)
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<pro key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
使用ProxyFactory
- 使用Spring AOP而不必依赖于Spring IOC
- 大多数情况下最佳实践是用IOC容器创建AOP代理
- 虽然可以硬编码方式实现,但是Spring推荐使用配置或注解方式实现
使用"auto-proxy"
- Spring也允许使用"自动代理"的bean定义,它可以自动代理选定的bean,这是建立在Spring的"bean post processor" 功能基础上的(在加载bean的时候就可以修改)
- BeanNameAutoProxyCreator
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator/">
<property name="beanNames" value="jdk*,onlyJdk"/>
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
</bean>
- DefaultAdvisorAutoProxyCreator,当前IOC容器中自动应用,不用显式声明引用advisor的bean定义