[java]44、Spring-04注解
1、拆分applicationContext.xml
分别创建applicationContext.xml
、applicationContext-mybatis.xml
两个文件
1.1、在applicationContext.xml
文件中通过标签导入实现
<import resource="applicationContext-mybatis.xml" />
1.2、在java
中导入多个xml
文件
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"applicationContext.xml",
"applicationContext-mybatis.xml");
1.3、使用classpath*
通配符
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath*:applicationContext*.xml");
1.4、注解实现bean
标签
在applicationContext.xml
设置需要扫描的包
<context:component-scan base-package="com.sj.domain" />
1、@Component
相当于<bean>
,通过value设置bean
的id
,默认会将类名的首字母小写形式作为bean
的id
import org.springframework.stereotype.Component;
@Component("person")
public class Person {
}
2、@Controller
用于标识控制器层,@Service
用于标识业务层等价于@Component
,@Repository
用于标志Dao
层
3、@Scope
:设置singleton
、prototype
@Lazy
:scope
为singleton
时延迟加载
<bean scope="singleton" lazy-init="true" />
4、@ComponentScan
:相当于<context:component-scan />
标签
@ComponentScans
:扫描多个包
例如扫描service
下面的包
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
@Component("person")
@ComponentScans({
@ComponentScan("com.sj.dao"),
@ComponentScan("com.sj.servlet")
})
@ComponentScan("com.sj.service")
public class Person {
}
5、@Autowired
:默认按照类型注入bean
实例
可以写在成员变量(不会调用setter
)、setter
、构造方式上
可以配合使用@Qualifier
、@Named
:设置需要注入的bean
的id
required
设置为false
:找不到对应的bean
时不会抛出异常
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class User {
private Dog dog;
@Autowired(required = true) // 找不到dog2类型会抛异常
@Qualifier("dog2") // 设置自动注入的类型名称是dog2
public void setDog(Dog dog) {
this.dog = dog;
}
}
在有参构造方法中参数也会有注入值
@Component
public class User {
private Dog dog;
public User(Dog dog) { // 自动注入dog的值
this.dog = dog;
}
}
6、@Value
用于注入String
、基本类型、BigDecimal
等类型
可以配合配置文件(比如properties
)使用${}
读取配置文件的内容
7、@PropertySources
、@PropertySource
相当于<context:property-placeholder>
@Component("dog2")
@PropertySource("dog.properties")
@PropertySources({
@PropertySource("user.properties")
})
public class Dog {
private String name;
@Value("${name}")
public void setName(String name) {
this.name = name;
}
}
2、注解其他功能实现
2.1、注解实现AOP
@Aspect
:用来标识一个切面类(切面类对象需要被放入到IoC
容器中)
@Around
:用来设置切入点(被注解的方法要有返回值、接收一个ProceedingJointPoint
参数)
还需要加上标签<aop:aspectj-autoproxy>
,代替之前的<aop:config>
同样有proxy-target-class
属性设置AOP
的底层实现方案
<aop:aspectj-autoproxy proxy-target-class="true" />
1、applicationContext.xml
代码如下
<context:component-scan base-package="com.sj" />
<aop:aspectj-autoproxy />
// 初始化切面类
<bean class="com.sj.aop.DefaultAspect" />
注解指明需要切入的对象
@Aspect
public class DefaultAspect {
@Around("within(com.sj.service.impl.UserServiceImpl)")
public Object log(ProceedingJoinPoint point) throws Throwable{
// 调用目标方法
Object ret = point.proceed();
return ret;
}
}
2、或者使用@Pointcut
配置切入点
@Aspect
public class DefaultAspect {
@Pointcut("within(com.sj.service.impl.UserServiceImpl)")
public void pc() { }
@Around("pc()")
public Object log(ProceedingJoinPoint point) throws Throwable{
Object ret = point.proceed();
return ret;
}
}
在applicationContext.xml
中配置切入函数
<aop:config>
<aop:pointcut id="pc" expression="com.sj.aop.DefaultAspect.pc()"/>
<aop:advisor advice-ref="logInterceptor" pointcut-ref="pc" />
</aop:config>
2.2、注解实现事务管理
1、@Transactional
可以在需要进行事务管理的类(比如Service
)上使用这个注解,代表所有方法都会自动管理事务
可以在需要进行事务管理的方法上使用这个注解(可以覆盖类中@Transactional
的配置)
可以设置isolation
、propagation
、timeout
、rollbackFor
、noRollbackFor
、readOnly
等属性
2、使用<tx:annotation-driven/>
取代之前的<tx:advice>
、<aop:config>
同样有proxy-target-class
属性设置AOP
的底层实现方案
<tx:annotation-driven transaction-manager="txMgr" />
2.3、@Configuration
、@Bean
1、@Configuration
:使用此注解的类,可以取代applicationContext.xml
,将注解类添加到IoC
容器中
它也是一个@Component
,所以可以通过component-scan
扫描
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
}
2、可以在@Configuration
类中,使用@Bean
修饰方法,进行bean
对象的创建
默认情况下,方法名就是bean
的id
。也可以通过name
、value
属性设置bean
的id
可以配置@Scope
设置bean
的创建次数
@Configuration
public class BeanConfig {
@Bean("skill2")
@Scope("prototype")
public Skill skill() {
return new Skill();
}
}
3、@Bean
方法的注入-bean
如果bean
属性本身有@Autowired
,那么直接new
原来对象即可
// Dog
@Component
public class Dog {}
// Person
public class Person {
private Dog dog;
@Autowired
public void setDog(Dog dog) { this.dog = dog }
}
// BeanConfig
@Configuration
public class BeanConfig {
@Bean
public Person person(Dog dog) {
return new Person();
}
}
Spring
会利用@Autowired
技术,自动注入bean
给@Bean
方法的参数
@Configuration
public class BeanConfig {
@Bean // 默认@Scope("singleton") IoC 容器创建时就会创建对象
public Dog dog() { return new Dog(); }
@Bean // 自动注入 Dog 的对象
public Person person(Dog dog) {
Person person = new Person();
person.setDog(dog);
person.setDog(dog()); // 调用多次,只new一个对象
person.setDog(dog());
return person;
}
}
@Bean方法被直接调用多次,也能保证是
singleton
,因为@Configuration
底层使用了CGLib
动态代理,对@Bean
方法进行了增强处理
// this指向的是代理对象
this = {BeanConfig$$EnhancerBySpringCGLIB$$6a559a7e@2260}
dog = {Dog@2258}
person = {Person@2259} "Person{dog=null}"
// 代理对象伪代码
public class BeanConfigCGLIB extends BeanConfig{
@Override
public Dog dog() {
if (容器里没有) {
return super.dog();
} else {
return 容器里的dog;
}
}
}
4、@Bean
方法注入-FactoryBean
@Bean
方法返回的是DogFactoryBean
,由于@Configuration
底层是用了CGLib
对@Bean
进行了增强处理,会调用@Bean
的getObject
方法
public class DogFactoryBean implements FactoryBean<Dog> {
@Override
public Dog getObject() throws Exception {
return new Dog();
}
@Override
public Class<?> getObjectType() {
return Dog.class;
}
}
@Configuration
public class BeanConfig {
@Bean
DogFactoryBean dog() {
return new DogFactoryBean();
}
}
5、@Bean
的方法注入-其他类型
@Configuration
@PropertySource("person.properties")
public class BeanConfig {
@Value("${age}")
private int age;
@Value("${name}")
private String name;
@Bean
public Person person() {
Person person = new Person();
person.setAge(age);
person.setName(name);
return person;
}
}
3、纯注解开发
3.1、工厂注解入口
如果创建的工厂入口是注解,一般就用AnnotationConfigApplicationContext
可以通过@Import
、@ComponentScan
扫描其他注解信息
可以通过@ImportSource
导入其他XML
配置文件
new AnnotationConfigApplicationContext("com.mj")
new AnnotationConfigApplicationContext(Person.class)
@Import({Person.class, BeanConfig.class})
public class Dog {
}
public class SkillTest {
public ApplicationContext ctx;
@Before
public void before() {
ctx = new AnnotationConfigApplicationContext(Person.class);
}
@Test
public void test() {
System.out.println(ctx.getBean("com.mj.domain.Person"));
}
}
通过@Import
导入创建bean
的id
是全类名
3.2、bean
的创建方式总结
1、在Spring
中,bean
有3中常见的创建方式
@Component
:常用于源码不可修改的类型(比如第三方库的类型)或者创建过程比较复杂的类型
@Bean
:常用于源码不可修改的类型(比如第三方库的类型)或者创建过程比较复杂的类型
<bean>
:适用于所有类型
当上述三种方式bean
的id
类型相同时,优先级如下:
<bean>
大于@Bean
大于@Component
因为<bean>
可以用于覆盖以前@Bean
、@Component
的内容,所有减少@Bean
、@Component
代码的变动
3.3、纯注解开发-AOP
@EnableAspectJAutoProxy
相当于<aop:aspectj-autoproxy />
@Configuration
@EnableAspectJAutoProxy // 取代<aop:aspectj-autoproxy />
@ComponentScan("com.mj")
public class BeanConfig { }
3.4、纯注解开发-MyBatis
@MapperScan
相当于MapperScannerConfigurer
// db.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test_mbatis
jdbc.username=root
jdbc.password=shijikl126
mybatis.typeAliasesPackage=com.mj.domain
mybatis.mapperLocations=mappers/*.xml
mybatis.mapperScan=com.mj.dao
@Configuration
@PropertySource("db.properties")
@MapperScan("${mybatis.mapperScan}")
public class MyBatisConfig {
@Value("${jdbc.driverClass}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${mybatis.typeAliasesPackage}")
private String typeAliasesPackage;
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage(typeAliasesPackage);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
return bean;
}
}
3.5、纯注解开发- 事务管理
@EnableTransactionManagement
相当于<tx:annotation-driven />
@Configuration
@EnableTransactionManagement
public class TxConfig {
@Bean
public DataSourceTransactionManager mgr(DataSource dataSource) {
DataSourceTransactionManager mgr = new DataSourceTransactionManager();
mgr.setDataSource(dataSource);
return mgr;
}
}
3.6、JSR
注解
1、JSR
是Java Specification Requests
的缩写,译为Java
规范提案
- 是指向
JSP(Java Community Process)
提出新增一个标准化技术规范的正式请求,任何人都可以提交JSR
,以向Java
平台增添新的API
和服务 - 一旦某个
JSR
通过了JCP
的审核,他就变成了Java
技术栈的一部分,可以安全第用于生成环境 -
JSR
审核过程确保了只有可靠稳定的技术才能变成Java
的一部分,避免过度臃肿和膨胀
2、Spring
也支持JSR
规范中定义的注解
3、需要导入的依赖
<dependency>
<groupId>javax.activation</groupId>
<artifactId>javax.activation-api</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
3、@Resource
、@Inject
-
@Resource
:根据名称进行注入,可以在成员变量setter
上
@Resource(name = "dog")
等价于@Autowired
、@Qualifier("dog")
-
@Inject
:默认根据类型进行注入,可以用在成员变量、setter
、构造方法上
可以配合使用@Qualifier
、@Name
:设置需要注入的bean
的id
public Person() {
System.out.println("Person-----------------");
}
@Inject
public Person(@Named("dog2") Dog dog, @Value("jack") String name) {
this.dog = dog;
this.name = name;
System.out.println("Person(dog, name)-----------------");
}
4、@PostConstructor
、@PreDestory
@PostConstructor
:在InitializingBean的afterPropertiesSet
方法之前调用
@PreDestory
:在DisposableBean
的destroy
方法之前调用
上述两个注解替代的方法是init-method
和destroy-method
@PostConstruct
public void postConstrut() {}
@PreDestroy
public void preDestory() {}
3.7、property-placeholder
的底层
它的底层使用了PropertySourcesPlaceholderConfigurer
类
可以使用这个类取代<context:property-placeholder />
、@PropertySources
、@PropertySource
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="db.properties" />
</bean>
@Bean
public PropertySourcesPlaceholderConfigurer configurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource("db.properties"));
return configurer;
}
3.8、component-scan
详解
exclude-filter
:设置不需要扫描的类型
include-filter
:设置需要扫描的类型
<context:component-scan base-package="com.sj" >
<context:exclude-filter type="assignable" expression="com.sj.domain.Dog"/>
<context:include-filter type="assignable" expression="com.sj.domain.Person"/>
<context:exclude-filter type="aspectj" expression="com.sj.service..*"/>
<context:exclude-filter type="regex" expression=".*er.*"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
被
include-filter
包含的类型,就算没有@Component
、<bean>
、@Bean
修饰,也会放到IoC
容器中
关于exclude-filter
、include-filter
的type
属性值,决定了expression
属性可以设置什么值
assignable
:设置具体类型
annotation
:设置注解类型
aspectj
:设置切入点表达式within
中的内容
regex
:设置正则表达式,比如.*service.*
表示全类名中包含service
的
custom
设置自定义类型过滤器(实现org.springframework.core.type.filter.TypeFilter
接口)
<context:exclude-filter type="custom" expression="com.sj.filter.MyTypeFilter"/>
// MyTypeFilter.java
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
System.out.println(metadataReader.getClassMetadata().getClassName());
String name = metadataReader.getClassMetadata().getClassName();
return name.matches(".*My.*"); // 过滤带有My字符的
}
}
3.9、@CommponentScan
@Component
@ComponentScan(basePackages = "com.sj", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Person.class, Dog.class}),
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*My.*"),
@ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "com.sj.service..*"),
})
public class MyCar {
}