SSM-Spring(六)-纯注解开发
实现AOP
- 与之前的注解实现AOP进行比对
@EnableAspectJAutoProxy相当于<aop:aspectj-autoproxy/>- 删除applicationContext.xml
-
新建入口类
//com.zh.cfg.AppConfig @Configuration //相当于<aop:aspectj-autoproxy/> @EnableAspectJAutoProxy @ComponentScan("com.zh") public class AppConfig { } -
测试入口
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); UserService service = ctx.getBean("userService", UserService.class); service.list(); - 其余不变
实现整合MyBatis
-
之前示例代码,使用xml整合如下
<!-- 1. 创建数据源 --> <!-- 加载配置文件 --> <context:property-placeholder location="db.properties"/> <!-- 数据源(Druid) --> <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}"/> </bean> <!--2.创建SqlSessionFactoryBean,获取sqlsession 设置数据源 设置别名 设置映射文件位置 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"/> <!-- 这个包底下的类会自动设置别名(一般是领域模型) 这个包下面所有的别名就是类名 比如com.zh.domain.Skill;别名是Skill --> <property name="typeAliasesPackage" value="com.zh.domain"/> <!-- 映射文件的位置 --> <property name="mapperLocations"> <array> <value>mappers/*.xml</value> </array> </property> </bean> <!-- 3. 扫描dao 设置sqlSessionFactory名字 告知到哪里去扫描dao --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 设置SqlSessionFactoryBean的id --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- 设置dao的包 --> <property name="basePackage" value="com.zh.dao"/> </bean> -
创建入口类MyBatisConfig
@Configuration //加载配置文件 @PropertySource("db.properties") //MapperScan相当于MapperScannerConfigurer @MapperScan("${mybatis.mapperScan}") public class MyBatisConfig { //纯注解注入基本数据类型 @Value("${jdbc.driverClassName}") 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; //创建数据源,因为DruidDataSource类是第三方的,所以通过@Bean方法 @Bean public DataSource dataSource() { DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driverClassName); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } //创建SqlSessionFactoryBean,获取sqlsession @Bean public SqlSessionFactoryBean sqlSessionFactory() throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource()); //设置别名 bean.setTypeAliasesPackage(typeAliasesPackage); //映射文件的位置 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); bean.setMapperLocations(resolver.getResources(mapperLocations)); return bean; } //扫描dao //可以直接使用MapperScan来代替 // @Bean // public MapperScannerConfigurer configurer() { // MapperScannerConfigurer configurer = new MapperScannerConfigurer(); //不用手动注入,可以直接通过参数类型自动注入 // configurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); //设置要扫描的dao // configurer.setBasePackage("com.zh.dao"); // return configurer; // } } -
配置文件内容如下:db.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test-mybatis jdbc.username=root jdbc.password=root mybatis.typeAliasesPackage=com.zh.domain mybatis.mapperLocations=mappers/*.xml mybatis.mapperScan=com.zh.dao -
SkillTest
@Before public void before() { ctx = new AnnotationConfigApplicationContext("com.zh"); skillDao = ctx.getBean("skillDao", SkillDao.class); } @Test public void list() { List<Skill> skills = skillDao.list(); System.out.println(skills); }
实现事务管理
-
之前xml与半注解实现事务管理如下
<!-- 上面是整合MyBatis,略 --> <!-- 事务管理器 --> <bean id="txMgr" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--设置注解驱动 取代之前的<tx:advice>、`<aop:config> --> <tx:annotation-driven transaction-manager="txMgr"/> <!--让spring扫描这个包下所有的类,用来解析注解--> <context:component-scan base-package="com.zh"/> <!-- 附加代码:事务管理代码 --> <!-- <tx:advice id="txAdvice" transaction-manager="txMgr">--> <!-- <tx:attributes>--> <!-- <tx:method name="*"/>--> <!-- <tx:method name="list*" propagation="SUPPORTS"/>--> <!-- </tx:attributes>--> <!-- </tx:advice>--> <!-- 切面 --> <!-- <aop:config>--> <!-- <aop:pointcut id="pc" expression="within(com.zh.service..*)"/>--> <!-- <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>--> <!-- </aop:config>--> - 现在就是如何使用注解替换上面的xml
-
创建一个单独的类实现:TxConfig
@Configuration //EnableTransactionManagement相当于<tx:annotation-driven /> //类型自动注入功能,不需要将事务管理器手动注入 @EnableTransactionManagement public class TxConfig { @Bean //创建事务管理器 //dataSource注入:由参数类型自动注入,容器中在mybatis中已经生成该对象 public DataSourceTransactionManager mgr(DataSource dataSource) { DataSourceTransactionManager mgr = new DataSourceTransactionManager(); mgr.setDataSource(dataSource); return mgr; } } -
测试入口
private ApplicationContext ctx; private SkillService skillService; @Before public void before() { ctx = new AnnotationConfigApplicationContext("com.zh"); skillService = ctx.getBean("skillService", SkillService.class); } @Test public void save() throws Exception { skillService.test(null); }
JSR注解
- JSR是Java Specification Requests的缩写, 译为Java规范提案
- 是指向JCP(Java Community Process) 提出新增一个标准化技术规范的正式请求
- 任何人都可以提交JSR,以向Java平台增添新的API和服务
- 一旦某个JSR通过了JCP的审核,它就变成了Java技术栈的一部分,可以安全地用于生产环境
- JSR的审核过程确保了只有可靠稳定的技术才能变成Java的一部分,避免过度臃肿和膨胀
- JSR就是定义的向java官方平台提出新增规范的建议的一套规范
- Spring也支持JSR规范中定义的一些注解
- JSR 250:
@Resource - JSR 330:
@Inject - JSR 520:
@PostConstruct、@PreDestroy - 上面是JSR规范的一些版本,Spring识别这些注解
- JSR 250:
-
导入依赖
<!-- JSR注解 --> <!-- @Resource、@PostConstruct、@PreDestroy --> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <!-- @Inject --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
@Resource、@Inject
@Resource:根据名称(id)进行注入,可以用在成员变量、setter上@Resource(name="dog")等价于@Autowired、@Qualifier("dog2")
//dog的名称(id) @Component("dog2") public class Dog {} @Component public class Person { //可以根据名称(id)进行注入 @Resource(name = "dog2") private Dog dog; //也可以修饰set方法 //@Resource(name = "dog2") //public void setDog(Dog dog) { // this.dog = dog; //} } //测试有dog属性值 System.out.println(ctx.getBean("person"));@Inject:默认根据类型进行注入, 可以用在成员变量、setter、构造方法上-
可以配合使用
@Qualifier、@Name:设置需要注入的bean的id//dog的名称(id) @Component("dog2") public class Dog {} @Component public class Person { //修饰成员:可以根据类型进行注入,不管名称是什么 //@Inject private Dog dog; private String name; //修饰set方法 //@Inject public void setDog(Dog dog) { this.dog = dog; } //默认会调用这个构造方法 public Person() { System.out.println("Person-------------"); } /* 修饰构造方法:有两个功能 1. 一旦使用@Inject修饰,就会首先调用这个构造方法 2.默认根据类型自动注入:会到容器中查找Dog类型的bean对象,name为非bean对象,直接传值,然后调用 * */ // @Inject // public Person(Dog dog, @Value("jack") String name) { // this.dog = dog; // this.name = name; // System.out.println("Person(Dog, String)-------------"); // } /* * 可以配合@Named,指定以名称(id)的方式注入 * 用@Qualifier也同样 * */ @Inject //@Named("dog2") :要按照名称(id)自动注入,不要按照类型自动注入 public Person(@Named("dog2") Dog dog, @Nullable String name) { this.dog = dog; this.name = name; // System.out.println("Person(Dog, String)-------------"); } //@Autowired也能用在构造方法中 // @Autowired // public Person(@Named("dog2") Dog dog, @Nullable String name) { // this.dog = dog; // this.name = name; // } } //测试有dog属性值 System.out.println(ctx.getBean("person"));
-
@PostContruct、@PreDestroy
@PostContruct:在InitializingBean的afterPropertiesSet方法之前调用@PreDestroy:在DisposableBean的destroy方法之前调用-
代码举例
//监听生命周期 @Component public class Processor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof Student)) return bean; System.out.println("BeanPostProcessor - before"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof Student)) return bean; System.out.println("BeanPostProcessor - after"); return bean; } } //Student生命周期 public class Student implements InitializingBean, DisposableBean, BeanNameAware, ApplicationContextAware { private String name; public void setName(String name) { this.name = name; System.out.println("setName"); } public void run() { System.out.println("run"); } public Student() { System.out.println("Student"); } public void init() { System.out.println("init"); } public void dealloc() { System.out.println("dealloc"); } //在InitializingBean的afterPropertiesSet方法之前调用 @PostConstruct public void postConstruct() { System.out.println("postConstruct"); } //在DisposableBean的destroy方法之前调用 @PreDestroy public void preDestroy() { System.out.println("preDestroy"); } @Override public void setBeanName(String name) { System.out.println("setBeanName"); } @Override public void destroy() throws Exception { System.out.println("DisposableBean - destroy"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean - afterPropertiesSet"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("setApplicationContext"); } -
测试
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); Student student = ctx.getBean("student", Student.class); student.run(); ctx.close(); -
打印Student生命周期调用顺序结果:
tudent setName setBeanName setApplicationContext BeanPostProcessor - before postConstruct InitializingBean - afterPropertiesSet init BeanPostProcessor - after run preDestroy DisposableBean - destroy dealloc
读取配置文件的底层实现
- 读取配置文件的方法:
- 方法一:
-
xml配置
<context:property-placeholder location="db.properties"/> -
类中直接使用
@Component public class UserServiceImpl { @Value("${jdbc.username}") private String name; }
-
-
方法二:注解
@Component @PropertySource("db.properties") public class UserServiceImpl { @Value("${jdbc.username}") private String name; }
- 方法一:
- property-placeholder的底层
- 它的底层使用了PropertySourcesPlaceholderConfigurer类
- 可以使用这个类取代
<context:property-placeholder/>、@PropertySources、@PropertySource -
使用方式一:用PropertySourcesPlaceholderConfigurer类的xml形式,相当于使用
<context:property-placeholder/><bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="location" value="db.properties"/> </bean> -
使用方法二:直接使用PropertySourcesPlaceholderConfigurer类的代码形式加载
@Configuration public class PropConfig { @Bean public PropertySourcesPlaceholderConfigurer configurer() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); configurer.setLocation(new ClassPathResource("db.properties")); return configurer; } } -
使用
@Component public class UserServiceImpl { @Value("${jdbc.username}") private String name; }
component-scan详解
-
exclude-filter设置不需要扫描的类型
<context:component-scan base-package="com.zh"> <!-- 排除Person类不扫描 --> <context:exclude-filter type="assignable" expression="com.zh.domain.Person"/> <!-- 排除com.zh.service这个包下面的所有类 --> <context:exclude-filter type="aspectj" expression="com.zh.service..*"/> <!-- 排除通过正则表达式匹配的所有类 --> <context:exclude-filter type="regex" expression=".*My.*"/> <!-- 排除带Service注解的所有类 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> <!-- 自定义过滤类,通过类match方法返回的值来过滤,返回true就过滤 --> <context:exclude-filter type="custom" expression="com.zh.filter.MyTypeFilter"/> </context:component-scan> //自定义过滤器类 public class MyTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String name = metadataReader.getClassMetadata().getClassName(); //返回true则过滤掉,反之。。。 return name.matches(".*My.*"); } } -
include-filter设置需要扫描的类型
<context:component-scan base-package="com.zh" use-default-filters="false"> <!-- 只扫描某个类--> <context:include-filter type="assignable" expression="com.zh.domain.Person"/> <!-- 自定义过滤类,通过类match方法返回的值来包含,返回true就包含--> <context:include-filter type="custom" expression="com.zh.filter.MyTypeFilter"/> </context:component-scan> - 注意: 被include-filter包含的类型,就算没有
@Component、<bean>、@Bean修饰,也会放到IoC容器中 - 关于exclude-filter、include-filter的type属性值,决定了expression属性可以设置什么值
- ssignable:设置具体类型
- annotation:设置注解类型
- aspectj:设置切入点表达式within中的内容
- regex:设置正则表达式,比如
.*service.*表示全类名中包含service的 - custom设置自定义类型过滤器 (实现org.springframework.core.type.filter.Type Filter接口)
-
@ComponentScan使用纯注解实现@Component //@ComponentScans() @ComponentScan(basePackages = "com.zh", excludeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = {Person.class, Dog.class}), @Filter(type = FilterType.ANNOTATION, classes = Service.class), @Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class), @Filter(type = FilterType.REGEX, pattern = ".*My.*"), @Filter(type = FilterType.ASPECTJ, pattern = "com.mj.service..*") }) public class MyCat {}