我的Spring学习记录(四)
虽然Spring管理这我们的Bean很方便,但是,我们需要使用xml配置大量的Bean信息,告诉Spring我们要干嘛,这还是挺烦的,毕竟当我们的Bean随之增多的话,xml的各种配置会让人很头疼。
所以我们需要一种东西来解决这个问题,这就是——注解,这次我们使用注解来将我们学过的所有东西重新做过一遍。
1. 了解注解
1.1 定义
注解是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
1.2 组成
- 元注解
包括@Retention、@Target、@Document、@Inherited四种 - jdk注解
包括@Override、@Deprecated、@SuppressWarnings - 自定义注解
自定义注解就是通过一定的规范,通过编写代码实现的
这里有几篇博文可供大家参考,其实了解注解可以让自己使用Spring的注解时不会很诧异,所以了解一下还是有好处的
http://www.cnblogs.com/Qian123/p/5256084.html
http://www.importnew.com/14479.html
http://www.cnblogs.com/yejg1212/p/3187362.html
http://www.cnblogs.com/lyy-2016/p/6288535.html
2. 加入maven依赖
这里的依赖于上篇博客的并没有什么不同之处,依赖主要如下:
<properties>
<spring.version>4.3.10.RELEASE</spring.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
3. Spring配置文件
添加了相关的依赖后我们现在就要对Spring进行配置了,以下便是classpath下的applicationContext.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:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--开启aspect注解-->
<aop:aspectj-autoproxy />
<!--加载db.properties配置文件-->
<context:property-placeholder location="db.properties"/>
<!--扫描包下的所有类,遇到相关注解会进行一些注册工作-->
<context:component-scan base-package="cn.lger"/>
<!--开启注解-->
<context:annotation-config/>
<!--配置DataSource-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--以下是数据库连接的具体信息 通过表达式将db.properties的信息注入-->
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
</bean>
<!--注册事务管理类-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解 注解添加事务管理类映射-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
上面的配置文件当中跟之前有所不同的就是添加了context约束,还有利用context加载了db.properties
,这是有关数据库的相关配置,之前是直接在配置文件写入,现在进行了提取,然后利用了context进行加载。
db.properties文件也是置于classpath下,具体内容是
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_demo_1
jdbc.username=root
jdbc.password=root
4. 使用注解装配Bean
好,经过上面的折腾之后就可使用注解开发我们程序了,接下来进入主题。
现在有一个场景,就是一个人拥有一只小狗叫旺财,如果参考之前的博客 我的Spring学习记录(二)就会发现我们需要配置的东西很多,现在我们用全新的方式来装配我们的Bean。现在我们给出实体,如下:
//这里为Man的超类,方便之后引申一些东西实现多态
public abstract class Human {
public abstract String myNameIs();
}
//注册为Spring的Bean Bean的名字叫man
@Component(value = "man")
public class Man extends Human{
@Value(value = "张三")
private String name;
//自动将Spring容器中Dog类型的Bean装配到dog
@Autowired
private Dog dog;
public String getUsername() {
return name;
}
public void setUsername(String username) {
this.name = username;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String myNameIs() {
return name;
}
public void noFreeTime(){
System.out.println("狗主人没有时间(public void noFreeTime())");
}
@Override
public String toString() {
return "Man{" +
"name='" + name + '\'' +
", dog=" + dog +
'}';
}
}
//注册为Spring的Bean
@Component(value = "dog")
public class Dog {
//注入值
@Value(value = "旺财")
private String name;
@Value(value = "土狗")
private String breed;
@Value(value = "公")
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", breed='" + breed + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
有了上面的准备工作之后,我们可以对我们的代码进行简单的测试了,我们现在不使用ClassPathXmlApplicationContext
来加载我们的Bean了,而是使用Spring test,同样的利用注解可以剩下很多事情,代码主要如下:
//junit运行的时候就会加载 SpringJUnit4ClassRunner,
//SpringJUnit4ClassRunner是Spring test提供的主要是方便测试
@RunWith(SpringJUnit4ClassRunner.class)
//加载Spring 配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class TestWired {
@Autowired
private Man man;
/**
* 测试注解装配是否成功
*/
@Test
public void test01(){
System.out.println(man);
}
}
//打印结果:Man{username='张三', money=9600.0, dog=Dog{name='旺财', breed='土狗', sex='公'}}
5. 使用注解完成AOP
现在我们的 Man
没有时间去看管我们的狗狗 Dog
了,迫切地需要找到一个帮忙照看狗狗的代理人 Proxy
,让他帮忙照看遛狗,Proxy
的代码实现如下:
@Aspect
//这里必须注册为Spring的Bean,虽然有了Aspect
//但是需要Spring调用这个切面还是需要注册为Spring的组件
@Component
public class Proxy {
@Pointcut("execution(* cn.lger.domain.Man.noFreeTime())")
public void noFreeTime(){}
@Before(value = "noFreeTime()")
public void before(){
System.out.println("代理人去被代理人家里牵狗,然后遛狗");
}
@After(value = "noFreeTime()")
public void after(){
System.out.println("代理人带狗狗回家");
}
}
看到了上面的代码之后想想我们之前使用xml配置切面是不是很简单了。
接下来我们测试一下我们的AOP是否能够成功
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAspect {
@Autowired
private Man man;
@Test
public void test01(){
man.noFreeTime();
}
}
//打印结果: 代理人去被代理人家里牵狗,然后遛狗
// 狗主人没有时间(public void noFreeTime())
// 代理人带狗狗回家
通过以上的测试我们基本上可以知道,与我们之前使用xml配置的AOP并没有什么结果上的不同,成功。
6. 使用注解完成Spring事务
好,通过AOP后我们接下来的就是利用AOP实现的Spring声明式事务
首先这里有 dao
和 service
@Component
public class TransferDao extends JdbcTemplate {
/**
* 将钱的数目转向当前user的账户
* @param money money
* @param username username
*/
public void transferMoneyToOne(Float money, String username){
this.update("UPDATE account SET money = money + (?) WHERE username = ?", money, username);
}
/**
* 通过有参构造函数注入DataSource
* @param dataSource dataSource
*/
@Autowired
public TransferDao(DataSource dataSource){
//因为没用xml注入dataSource所以这里需要自己设置
//调用超类的构造函数设置dataSource
super(dataSource);
}
}
@Component
public class TransferService {
@Autowired
private TransferDao dao;
/**
* 转账业务,这里为了方便直接写了张三转给了李四100块钱
*/
@Transactional
public void transferMoney(){
dao.transferMoneyToOne((float) -100, "张三");
// int i = 1 / 0;//这里模仿转账过程中可能出现的异常,测试时可以去掉注释用于测试事务是否成功执行
dao.transferMoneyToOne((float) 100, "李四");
// throw new RuntimeException("这里突然死机了");
}
public TransferDao getDao() {
return dao;
}
public void setDao(TransferDao dao) {
this.dao = dao;
}
}
下面我们进行测试工作来看看代码是否能够成功执行事务性的操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransaction {
@Autowired
private TransferService transferService;
/**
* 测试转账事务
*/
@Test
public void test01(){
transferService.transferMoney();
}
}
经过我们的测试证明当没有出现异常的情况张三成功给了李四转账了,当我们去掉注释出现异常结果是转账不成功。注解式的Spring事务配置就先是这么多
7. 总结
-
注解替代我们的xml配置可以帮助我们节省下很多的开发步骤,提高我们的开发效率,但是注解也是有一定的缺陷的,比如有没有觉得我们好好的xml把所有的东西都放在一起看着就能够集中一点,而注解就是相对的分散,这样可能我们维护的时候就会在各个类直接的注解中转来转去。所以各有各的好处。
-
Spring的注解远远不止是这么多,比如装配我们的Bean的Autowired在某种情况下就会出错,比如我们多态的实现,有两个子类作为组件,那么它要注入哪个?? 所以需要一个东西去辅助它,声明是哪一个Bean要被注入,除了Autowired装配还有Resource、Inject等;
装配之外还有其他的注解都需要学习一下,但是这篇的长度就会很长很长了,所以我就没有详细的写明,我给出一些链接,希望给大家的学习节省一些时间,如果之后我闲着的时候我将会以这篇为导引,写多一些注解来一起学习
Spring注解相关博文
http://blog.csdn.net/xyh820/article/details/7303330/
http://blog.csdn.net/u010987379/article/details/52152795
http://www.cnblogs.com/szlbm/p/5512931.html
http://blog.csdn.net/zheng0518/article/details/49206847
http://blog.csdn.net/lindonglian/article/details/47043727
http://www.cnblogs.com/biehongli/p/6640406.html
以上的代码可以在Github下载