1. 导入依赖
junit和spring-test
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.16.RELEASE</version>
</dependency>
2. 注解的作用
- 可以通过在.java文件中添加注解,可以替代spring-config.xml配置文件中的数据源、组件扫描等文件
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定Spring在初始化容器时哟啊扫描的包,作用和Spring的xml配置文件中的<context:component-scan base-package=“com.lyx”/>一样 |
@Bean | 使用在方法上,标注将该方法的返回值存储到Spring容器中 ,如@Bean(“dataSource”),Spring会将当前方法的返回值以指定名称(dataSource)存储到Spring容器中 |
@PropertySource | 用来加载.properties文件中的配置,作用和Spring的xml配置文件中的 <context:property-placeholder location=“classpath:jdbc.properties”/>一样 |
@Import | 用于导入其他配置文件,作用和Spring的xml配置文件中的<import resource=“classpath:spring.xml”/>一样,@Import({XXX.class,XXX.xml})可以导入多个配置文件 |
3. 注解形式,给IOC容器中存放Bean
@Configuration、@ComponentScan、@Bean、@PropertySource、@Import
必须要有@Configuration注解(配置类)
3.1 注解替换Dao层
原xml文件如下,我们是不用写例如UserDaoImpl等UserDao的实现类的,使用注解开发却需要写,因为注解是添加在实现类上面
<?xml version="1.0" encoding="utf8" ?>
<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="user" class="com.lyx.entity.User">
<property name="id" value="1"></property>
<property name="city" value="China"></property>
</bean>
</beans>
注解添加在实现类上面
import com.lyx.dao.UserDao;
import org.springframework.stereotype.Repository;
//替换xml文件的<bean id="userDao" class="com.lyx.entity.UserDao"></bean>
@Repository("userDao")//由于是Dao层,把@Component替换成@Repository
public class UserDaoImpl implements UserDao{
public User queryUserById(int id){
//数据库查询
}
}
3.2 注解替换Service层
//替换xml文件的<bean id="userService" class="com.lyx.entity.UserService"></bean>
@Service("userService")//由于是Service,把@Component替换成@Service
public class UserServiceImpl implements UserService{
@Resource(name="userDao")//跟Dao注解对应,替换原先注解@Autowired、@Qualifier("userDao")
private UserDao userDao;
public User queryUserById(int id){
userDao.queryUserById(id);
}
//get和set方法也可以不写,
}
3.3 注解替换数据源
<?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd "
>
<!-- placeholder占位符-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="${jdbc.init}"/>
<property name="maxWait" value="60000"/>
<property name="timeBetweenEvictionRunsMillis" value="50000"/>
<property name="minEvictableIdleTimeMillis" value="3000"/>
</bean>
<context:component-scan base-package="com.lyx"/>
</beans>
DataSourceConfiguration.java
//标志该类是Spring的核心配置类
@Configuration
//替换xml文件的<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
//替换xml文件的组件扫描<context:component-scan base-package="com.lyx"/>
@ComponentScan("com.lyx")
public class DataSourceConfiguration{
@Bean("dataSource")//Spring会将当前方法的返回值以指定名称存储到Spring容器中
public DataSource getDataSource() throws PropertyVetoException{
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("${jdbc.driverClass}");
dataSource.setUrl("${jdbc.url");
dataSource.setUsername("${jdbc.username}");
dataSource.setPassword("${jdbc.password}");
return dataSource;
}
}
- 把上面的DataSourceConfiguration.java配置类,import导入SpringConfiguration.java类文件
@Configuration
@Import({DataSourceConfiguration.class})
public class SpringConfiguration{
}
3.4 扫描器:@ComponScan注解,给扫描指定规则
ctrl键进入excludeFilters看看如何定义不扫描
ctrl进入FilterType,看看过滤的类型都有哪些
看注释:
public enum FilterType {
ANNOTATION, //按照注解过滤
ASSIGNABLE_TYPE, //按照类型过滤
ASPECTJ,//按照ASPECTJ表达式过滤
REGEX,//按照正则表达式过滤
CUSTOM;//按照自定义的过滤规则过滤
private FilterType() {
}
}
具体使用看这篇文章:Spring中FilterType的说明
ANNOTATION, 按照注解过滤
ASSIGNABLE_TYPE,按照类型过滤使用如下:
import com.lyx.mapper.StudentMapper;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Controller;
@Configuration
@ComponentScan(basePackages = "com.lyx",excludeFilters ={
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = StudentMapper.class)
})
@PropertySource(value = "database.properties")
public class MyConfiguration {
}
测试一下:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfiguration.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
再来看按照自定义类过滤CUSTOM:
先写一个自定义的类MyFilter
public class MyFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//如果扫描器扫描“com.lyx”包中的所有类,getClassName()方法可以拿到扫描的所有类名
String className = classMetadata.getClassName();
//不要与学生相关的三层组件
if(className.contains("Student")){
return true;
}
return false;
}
}
刚刚是过滤,现在来看只包含includeFilters
@Configuration
@ComponentScan(basePackages = "com.lyx",includeFilters ={
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = StudentMapper.class)
},useDefaultFilters = false)
@PropertySource(value = "database.properties")
public class MyConfiguration {
}
3.5 条件注解@Conditional
- 自定义条件类MyConditional,只有满足以下条件,才把当前Bean放入容器中,必须实现Condition接口
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取环境
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("StudentServiceImpl.type");
if (property.contains("Student")){
return true;
}
return false;
}
}
测试:
操作如下:
再次运行:
注意:注解形式给IOC加入Bean,全部在Myconfiguraion类中,即添加注解@Configuration的类
4. 回顾一下给Ioc加入bean的方法
三层组件:通过扫描器@ComponentScan,三层注解:@Repository @Service @Controller
非三层组件:
- @Bean + 返回值
- @import
- FactoryBean(工厂Bean)
第一种方式@Bean+返回值已经说过了
现在来看@import的使用
5. @Import注入Bean
-
直接把需要放入SpringIOC容器中的编写到@Import中
测试一下是否放入:输出全部的bean
②自定义ImportSelector接口的实现类,通过selectimports方法实现。并且告知程序自己编写的实现类。@Import({Orange.class MyImportSelector.class})
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.lyx.entity.CourseFactory","com.lyx.aop.MyAround"};
}
}
【selectImports()方法的返回值就是要纳入IoC容器的Bean】
最后来看第三种方式
6. FactoryBean注入Bean
- 自定义类MyFactoryBean,实现FactoryBean接口
package com.lyx.annotation;
import com.lyx.entity.Student;
import org.springframework.beans.factory.FactoryBean;
public class MyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new Student();
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
- 在MyConfiguration类中:添加注解@Import(value = {MyImportSelector.class})
package com.lyx.annotation;
import com.lyx.entity.Classroom;
import com.lyx.entity.Student;
import com.lyx.mapper.StudentMapper;
import com.lyx.service.StudentService;
import com.lyx.service.impl.StudentServiceImpl;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;
@Configuration
@ComponentScan(basePackages = "com.lyx",excludeFilters ={
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
@ComponentScan.Filter(type = FilterType.CUSTOM,value ={MyFilter.class})
})
@PropertySource(value = "database.properties")
@Import(value = {MyImportSelector.class})
public class MyConfiguration {
@Bean
public FactoryBean<Student> myFactoryBean(){
return new MyFactoryBean();
}
@Bean
@Conditional(MyCondition.class)
public StudentService getStudentServiceImpl(){
return new StudentServiceImpl();
}
}
测试一下:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfiguration.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
当前方法把myFactoryBean放到容器中了??
我们尝试通过bean的id值来拿
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfiguration.class);
Object myFactoryBean = context.getBean("myFactoryBean");
System.out.println(myFactoryBean);
}
}
//运行结果:Student{stuId=0, grade='null', Name='null', classId=0}
所以getBean拿到的是MyFactoryBean类中getObject()方法的返回值Student对象。
那么怎么拿MyFactoryBean对象呢
在getBean()时加上&符号
注意:需要通过&区分获取的对象是哪一个︰
不加&,获取的是最内部真实的Student;
如果加了&,获取的是FacotryBean
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfiguration.class);
Object myFactoryBean = context.getBean("&myFactoryBean");
System.out.println(myFactoryBean);
}
}