Spring-Day02
一、spring的注解开发和xml开发的优略势-老程序员喜欢问的面试题
优点:使用注解的形式替代xml配置,将繁杂的spring配置文件从工程中彻底消除掉,简化书写。
弊端:注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量
二、注解开发
1. 开启注解扫描-设置扫描的包
在applicationContext.xml 配置文件中开启注解扫描
<!--启动注解驱动,指定对应扫描的路径,也就是资源所在的包-->
<context:component-scan base-package="com.itheima"/>
- 注:设置包扫描的时候范围尽可能的小
2. 在对象的类上添加对象的注解
-
controller层
- @Controller
-
service层
- @Service
-
dao层
- @Repository
-
除了以上三层以外的对象
- @Component
Spring就是通过扫描指定包下的有这些注解的类,把它变成bean加入到IoC容器中,再通过ctx.getBean()获取出来使用。
在UserServiceImpl上加注解
@Component("userService")
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void save() {
System.out.println("UserServiceimpl 运行了....");
}
}
使用测试类测试
public class AppTest2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.save();//可以正常执行
}
}
3. 在类上添加了注解以后都会有一个默认的id,默认的id就是类名首字母小写,如果你觉得不好你可以自己去指定
4. 其他的注解
-
@Scope(类注解器)
-
如果在类上不配置该注解默认为单例
//设定bean的作用域 @Scope("singleton") public class UserServiceImpl implements UserService {}
-
-
@PostConstruct(方法注解)在初始化方法上
//设定bean的生命周期 @PostConstruct public void init(){}
-
@PreDestroy(方法注解)销毁方法上
//设定bean的生命周期 @PreDestroy public void destroy(){}
5. 整合第三方的资源对象
-
- 编写一个方法返回一个核心对象
-
- 在方法上添加@Bean(“id”)
-
- 当前的类上添加一个注解
-
@Component
在com.itheima.config中创建一个JDBCConfig类 //添加Component注解 @Component public class JDBCConfig { //在方法上添加@Bean("id") @Bean("dataSource") public DruidDataSource getDataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/Spring_db"); ds.setUsername("root"); ds.setPassword("root"); return ds; } }
在测试类中获取数据源成功执行
DruidDataSource dataSource = (DruidDataSource) ctx.getBean("dataSource"); System.out.println(dataSource);
说明:
-
因为第三方bean无法在其源码上进行修改,使用@Bean解决第三方bean的引入问题
-
该注解用于替代XML配置中的静态工厂与实例工厂创建bean,不区分方法是否为静态或非静态
-
@Bean所在的类必须被spring扫描加载,否则该注解无法生效
6. 属性注解
-
- 注入一个普通的数据
-
@Value
@Value("100") private int num; public void save() { System.out.println("num=" + num);//结果输出:num=100 }
-
- 注入一个对象属性
-
@Autowired
-
- Autowired注入的时候先按照类型进行匹配,如果类型能匹配上则直接注入,如果类型不能匹配(一个类有多个对象),则接着使用id进行匹配,id就是属性的名字
-
@Autowired
private UserDao userDao;
public void save() {
userDao.save();//这样就可以调用Dao了
}
```- 2. 如果出现id也不能匹配的情况,则使用@Qualifier来区分 ```java @Autowired @Qualifiler("userDao1")//指定加载的id private UserDao userDao; public void save() { userDao.save();//这样就可以调用Dao了 } ``` - 3. @Primary提交对象的使用优先级 ```java @Component @Primary//优先使用这个实现类 public class BookDaoImpl2 implements BookDao { public void save() { System.out.println("book dao running...2"); } } ```
-
@Resource
- 现在几乎不在使用,原因是jdk1.8以后不在支持该注解
7. 加载外部的properties配置文件
-
- 在类上添加@PropertySource
-
- 使用@Value("${key}")的形式去获取并注入给其他的属性
@Component("userDao") @PropertySource("classpath:jdbc.properties") //有多个配置文件用下面数组形式,ignore为找不到忽略找不到文件错误提示 @PropertySource(value={"classpath:jdbc.properties","classpath:abc.properties"},ignoreResourceNotFound = true) public class UserDaoImpl implements UserDao { @Value("${username}") private String username; @Value("${password}") private String password; public void save() { System.out.println("Userdao运行了......."+username+" "+password); } }
-
注意:
- 不支持*通配格式,一旦加载,所有spring控制的bean中均可使用对应属性值
- ignoreResourceNotFound:如果资源未找到,是否忽略,默认为false
8. 配置一个配置类并开启注解扫描
-
@Configuration
- 添加在类上面的-用来标识当前的类就是一个配置类
-
@ComponentScan
- 开启注解扫描的
@Configuration @ComponentScan("com.itheima") public class SpringConfig { }
-
测试类中创建使用AnnotationConfigApplicationContext
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); UserDao userDao = (UserDao) ctx.getBean("userDao"); userDao.save();
9. 在配置类上通过@import注解引入另一个配置类
-
@Import({JDBCConfig.class})
@Import(JDBCConfig.class) public class SpringConfig { } //导入多个使用 @Import({JDBCConfig.class,ABC.class})
-
说明:
-
在被导入的类中可以继续使用@Import导入其他资源(了解)
-
@Bean所在的类可以使用导入的形式进入spring容器,无需声明为bean
-
10. bean的控制
-
- 普通对象创建的顺序-面试题
-
xml: xml在创建对象的时候会按照xml配置的顺序进行创建,如果你想先创建某一个对象你只需要调整他的配置的顺序即可
-
注解: @DependsOn 标注的顺序
-
@DependsOn(“beanId”)
@DependsOn("userService")//依赖userService,比userService后加载 public class UserDaoImpl implements UserDao { }
-
- 对象创建的时机(延迟加载)
- @Lazy
- 位置:bean定义的位置(类上或方法上)
- 控制bean的加载时机,使其延迟加载
-
- 配置对象的创建顺序
-
@Order(数字)
@Order(2) public class SpringConfig { }
-
数字越小优先级越高
三、使用注解的形式来整合mybatis
整合分析图:
1. 导入jar包
2. 使用@Configuration来编写一个主配置类
@Configuration //新建Springconfig类,在类上添加注解
3. 在主配置类上添加注解扫描
@ComponentScan("com.itheima")//扫描注解
4. 编写一个jdbc的配置并导入jdbc.properties配置文件
-
-
导入配置文件
@PropertySource("classpath:jdbc.properties")
-
-
- 整合druid的核心配置类
-
- 从spring容器中获取jdbc的连接数据
-
- 编写一个方法创建druid的核心对象并返回同时在方法上添加@Bean(“dataSource”)
//创建一个JDBCConfig 配置类,配置数据源 public class JDBCConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean("dataSource") public DataSource getDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
5. 在主配置类中使用@import注解导入jdbc的配置类
//最终的Spring配置文件
@Configurable
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MybatisConfig.class})
public class SpringConfig {
}
6. 编写一个mybatis的配置类-创建2个对象
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setDataSource(dataSource);
ssfb.setTypeAliasesPackage("com.itheima.domain");
return ssfb;
}
@Bean
public MapperScannerConfigurer getMapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
7. 在主配置类中使用@import注解导入mybatis的配置类
@Import({JDBCConfig.class,MybatisConfig.class})
public class SpringConfig {
}
8.Service层配置
@Service("accountService") //声明bean
public class AccountServiceImpl implements AccountService {
//注入Dao对象
@Autowired
private AccountDao accountDao;
}
9.测试
public class AppTest {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
AccountService accountService = (AccountService) ctx.getBean("accountService");
List<Account> all = accountService.findAll();
System.out.println(all);
}
四、 spring整合junit做单元测试
1. 导入jar包
<!--junit-4.12-->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!--spring-test-->
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
2. 在test目录下编写测试类并在类上添加2个注解
3. 在测试类里面使用@Autowired注入你想测试的类
//设定spring专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//设定加载的spring上下文对应的配置
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
Account ser = accountService.findById(2);
Assert.assertEquals("关羽",ser.getName());
}
}
五、 spring排除某一些类和注解
1. 使用spring提供好的功能
//在Spring配置文件扫描器上面加excludeFilters过滤器
@ComponentScan(
value = "com.itheima", //设置基础扫描路径
excludeFilters = @ComponentScan.Filter( //设置过滤规则,excludeFilters为排除过滤
type= FilterType.ANNOTATION, //设置过滤器//设置过滤方式为按照注解进行过滤
classes = Service.class ///设置具体的过滤项,过滤所有@Repository修饰的bean
)
)
- includeFilters:设置包含性过滤器
- excludeFilters:设置排除性过滤器
- ype:设置过滤器类型
2. 自定义过滤器
-
- 编写一个类实现TypeFilter接口
public class MyTypeFilter implements TypeFilter {
@Override
//加载的类满足要求,匹配成功
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//通过参数获取加载的类的元数据
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//通过类的元数据获取类的名称
String className = classMetadata.getClassName();
//如果加载的类名满足过滤器要求,返回匹配成功
if(className.equals("com.itheima.service.impl.UserServiceImpl")){
//返回true表示匹配成功,返回false表示匹配失败。
//此处仅确认匹配结果,不会确认是排除还是加入,排除/加入由配置项决定,与此处无关
return true;
}
return false;
}
}
-
- 在ComponentScan中配置
@ComponentScan(
value = "com.itheima",
excludeFilters = @ComponentScan.Filter(
type= FilterType.CUSTOM,
classes = MyTypeFilter.class
)
)
3.自定义导入器
-
1.新建自定义导入器类
public class MyImportSelector implements ImportSelector { public String[] selectImports(AnnotationMetadata icm) { return new String[]{"com.itheima.dao.impl.AccountDaoImpl"}; } }
-
在SpringConfig配置类上添加导入
@Configuration @ComponentScan("com.itheima") @Import(MyImportSelector.class) //把自定义的导入器加载上来 public class SpringConfig { }
4.自定义注册器
-
实现创建一个类实现ImportBeanDefinitionRegistrar类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry r) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(r, false); TypeFilter tf = new TypeFilter() { public boolean match(MetadataReader mr, MetadataReaderFactory mrf) throws IOException { return true; } }; scanner.addIncludeFilter(tf); scanner.scan("com.itheima"); } }
-
导入自定义的注册器
//自定义注册器就相当于@ComponentScan的功能 @Import(MyImportBeanDefinitionRegistrar.class) //把自定义的注册器加载上来 public class SpringConfig { }