配置的三种写法:XML-based configuration 基于 XML 文件,Annotation-based configuration 基于 Annotation ,以及 Java-based configuration 基于 Java 代码配置。
DI 注解(Autowired)
需要配置 DI 注解解析器,Spring3.0之前,需要手动配置 Autowired 注解的解析程序,Web开发中必须配置。<context:annotation-config/>
Autowired 注解和 Resource 注解
Autowired 注解和 Resource 注解都可以完成相同功能的操作,Autowired 是 Spring 提高,Resource 是 JavaEE 提供。(spring-context 包下)
相同点:
- 自动的把属性需要的对象找出来,自动注入
- 贴在字段或者 setter 方法上面,一般贴在字段上,贴字段上不需要使用set方法。
可以注入一些 Spring 内置的重要对象,甚至是 Servlet 的API,比如:BeanFactory、ApplicationContext、ServletContext等
不同点:
Autowired 和 Resource 注解都必须要能找到对应的对象,否则报错。Autowired 注解可以通过 required=false 来避免这个问题(required=false)
Autowired 按照类型找,找不到按名字找,可以配合 @qualifier限定
注解使用,表示直接查找这个名字的对象
1 2 3 4 5
| public class Somebean { @Autowired @Qualifier("otherbean") private Otherbean other; }
|
Reource 按名字找,找不到按类型找。
1 2 3 4
| public class Somebean { @Resource(name = "otherbean") private Otherbean other; }
|
原来的方式:
1 2 3 4 5
| <context:annotation-config/> <bean id="otherbean" class="cn.lizhaoloveit.annotation.Otherbean"/> <bean id="somebean" class="cn.lizhaoloveit.annotation.Somebean"> <property name="other" ref="otherbean"></property> </bean>
|
现在的方式:
1 2
| <bean id="otherbean" class="cn.lizhaoloveit.annotation.Otherbean"/> <bean id="somebean" class="cn.lizhaoloveit.annotation.Somebean"/>
|
需求
把 OtherBean 对象设置给 SomeBean 对象的 other 属性。
Value注解
Autoweired 和 Resource 注解用于注入对象,Value注解用于注入常量数据。
server.properties 文件
1 2
| server.port=8888 server.path=/
|
Java 代码:
1 2
| @Value("${server.port}") private int port;
|
引入配置文件
1
| <context:property-placeholder location="classpath:db.properties,classpath:server.properties"/>
|
或者
1 2
| <context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true"/> <context:property-placeholder location="classpath:server.properties" ignore-unresolvable="true"/>
|
IoC 注解(bean)
在xml文件配置了<context:component-scan>
标签后,spring容器可以自动去扫描base-pack所指定的包或其子包下面的java类文件,如果扫描到有@Component、@Controller、@Service 、@Repository等注解修饰的Java类,则将这些类注册为spring容器中的bean。
IoC 的注解解释器配置
1 2 3 4
| <context:annotation-config/>
<context:component-scan base-package="cn.lizhaoloveit.ioc"/>
|
@Repository
用于标注数据访问组件,即 DAO 组件。
@Service
用于标注业务层组件,即 Service 组件
@Controller
只用于标注控制层组件(Spring MVC 的 Controller)
@Component
泛指组件,当组件不好归类的时候,可以使用跟这个注解。
默认 id 是把被标注类名的首字母小写后的类名。
需要配置 IoC 注解的解析器:
context:component-scan base-package=""
表示去哪个包中及其子包中扫描组件注解
代码:
1 2 3 4 5 6 7 8 9 10 11
| @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class App { @Autowired private Somebean somebean; @Test public void testName() throws Exception { System.out.println(somebean); } }
|
1 2 3 4 5 6 7 8 9 10 11
| @ToString @Component public class Somebean { @Autowired @Qualifier("other") private Otherbean other; }
@Component("other") public class Otherbean { }
|
最后输出:Somebean(other=cn.lizhaoloveit.ioc.Otherbean@1d76aeea)
原理如下:
在类型上贴注解 @Componenet("other")
相当在配置文件中加入如下配置
<bean id="other" class="全限定名"
在字段上贴注解,@Autowired,默认会先找 Otherbean 类型的值注入,如果找不到会找 otherbean
id 名字的值注入,如果都没有会报错。如果加入注解 @Qulifier("other")
,表示会加载指定 id为 other 的bean 对象注入。
作用域注解(scope)
1 2 3 4
| @Component("other") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class Otherbean { }
|
相当于:<bean id="other" class="...Otherbean" scope="singleton"/>
初始化和销毁注解
1 2 3 4 5 6 7 8 9 10 11 12
| @Component("other") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class Otherbean { @PostConstruct public void open() { System.out.println("初始化方法"); } @PreDestroy public void close() { System.out.println("关闭"); } }
|
1 2 3
| <bean id="other" class="...Otherbean" scope="singleton" init-method="open" destro-method="close" />
|
AOP注解
AOP的注解解释器:
1 2 3 4 5 6
| <context:annotation-config/>
<context:component-scan base-package="cn.lizhaoloveit.ioc"/>
<aop:aspectj-autoproxy/>
|
以前基于 xml 的做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <import resource="classpath:applicationContext.xml" />
<context:annotation-config/>
<context:component-scan base-package="cn.lizhaoloveit.aopjdkproxy"/>
<aop:aspectj-autoproxy proxy-target-class="false"/>
<bean id="txManager" class="cn.lizhaoloveit.aopjdkproxy.TransactionManager"/> <aop:config proxy-target-class="false"> <aop:aspect ref="transactionManager"> <aop:pointcut expression="execution(* cn.lizhaoloveit.aop.service.*Service.*(..))" id="txPointcut"/> <aop:before method="begin" pointcut-ref="txPointcut"/> <aop:after-returning method="commit" pointcut-ref="txPointcut"/> <aop:after-throwing method="rollback" pointcut-ref="txPointcut" throwing="exception"/> <aop:after method="close" pointcut-ref="txPointcut"/> <aop:around method="around" pointcut-ref="txPointcut"/> </aop:aspect> </aop:config>
|
<aop:aspect>
标签的作用是关联项目中的增强模块,这时只需要在增强类上贴上注解即可。@Aspect
默认是按类型查找。
1 2 3
| @Aspect @Component public class TransactionManager
|
<aop:pointcut expression="execution(* cn.lizhaoloveit.aop.service.*Service.*(..))" id="txPointcut"/>
标签的作用是找到业务流的切入点,我们可以直接在增强模块类(TransactionManager)中使用注解来表示
1 2 3 4
| @Pointcut("execution(* cn.lizhaoloveit.aop.service.*Service.*(..))") public void txPointcut() { }
|
上面的代码表示的含义是,拦截 execution 中的所有方法调用,并且给该 execution 设置一个 id值为 方法名 txPointcut。
最后只要规定切入点的位置和增强方法即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Aspect @Component public class TransactionManager { @Pointcut("execution(* cn.lizhaoloveit.aop.service.*Service.*(..))") public void txPointcut() { } @Before("txPointcut()") public void begin(JoinPoint jp) { System.out.println("事务开始..."); } @AfterReturning("txPointcut()") public void commit(JoinPoint jp) { System.out.println("当前连接点签名 :" + jp.getSignature()); System.out.println("提交事务..."); } @AfterThrowing("txPointcut()") public void rollback(JoinPoint jp, Throwable exception) { System.out.println("当前连接点签名 :" + jp.getSignature()); System.out.println("回滚..." + exception.getMessage()); } @After("txPointcut()") public void close() { System.out.println("关闭资源..."); } @Around("txPointcut()") public Object around(ProceedingJoinPoint pjp) { Object res = null; begin(pjp); try { System.out.println("执行目标方法"); res = pjp.proceed(); commit(pjp); } catch (Throwable e) { e.printStackTrace(); rollback(pjp, e); } finally { close(); } return res; } }
|
ProxyApp-context.xml
1 2 3 4
| <import resource="classpath:applicationContext.xml" />
<context:component-scan base-package="cn.lizhaoloveit.aopjdkproxy"/> <aop:aspectj-autoproxy proxy-target-class="false"/>
|
总结:xml 配置文件,通过 IoC 注解解析器告诉 Spring 容器去扫描 base-package 包下的 java 类文件,<aop:aspctj-autoproxy>
配置 aop 注解解释器,扫描时,发现了 TransactionManager,带有 @Aspect,并且扫描到 @Component,就会创建 Transaction对象,并且将其作为切面模块,插入哪个业务流程呢?由 @Pointcut 告诉动态代理对象去拦截哪些方法,去在合适的时机执行增强方法。
Tx注解
一般用于 list、get、query 开头的 Service 方法使用 事务的 read-only 模式。
Transactional 注解
具体使用:
1 2 3 4
| <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="txManager" />
|
Transactional 贴在业务类上,也可以贴在业务类方法之上:
- 贴在类上:表示该类中所有的方法都使用 Transactional 注解的属性配置
- 贴在方法上:只针对被贴的这一个方法,一般用于做单独配置
一般的,我们可以在业务类上直接贴该注解,并在查询方法上设置 readOnly 属性为 true,一定要开启 Tx 注解的解释器
配置 JDBC 事务管理器,使用 CGLIB
1 2 3 4 5 6 7 8 9 10 11 12 13
| <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" <property name="dataSource" ref="dataSource"/> </bean>
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
|
1 2 3 4 5 6 7 8 9 10 11
| @Service @Transactional public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDAO dao; public void trans(Long outId, Long inId, int money) { dao.transOut(outId, money); int a = 1 / 0; dao.transIn(inId, money); } }
|
SpringMVC 注解(Controller、RequestMapping)
一个简单的基于注解开发的 SpringMVC,
- 需要在 web.xml 中设置一个调度器 dispatcherServlet。
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:mvc2.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
|
1 2 3 4
| <context:component-scan base-package="cn.lizhaoloveit.hello"/>
<mvc:annotation-driven/>
|
IoC 注解解释器,会扫描其下面的注解标签,遇到 @Component、@Controller、@Service 、@Repository,会生成对应的 <bean>
实例对象,
1 2 3 4 5 6 7 8 9 10 11 12
| @Controller public class UserController {
@RequestMapping("/abc") public ModelAndView sayHello() { System.out.println("...hello..."); ModelAndView mv = new ModelAndView(); mv.addObject("username", "will"); mv.setViewName("WEB-INF/view/hello/hello.jsp"); return mv; } }
|
@RequestMapping(“/abc”),与url做映射,处理器中可以写多个。
问题,理论上讲,mvc.xml 需要配置4个东西
- 处理器映射器 BeanNameUrlHandlerMapping
- 处理器适配器 SimpleControllerHandlerAdapter
- 视图解析器 InternalResourceViewResolver
- 处理器 handler
为什么使用注解后,我们就创建了 Controller,就可以使用了呢?
因为在 spring-webmvc.jar 包中,有一个 properties 文件,
/org/springframework/web/servlet/DispatcherServlet.properties
,

Mybatis-Spring 注解
创建 Mapper 代理对象,以前的做法:
1 2 3 4 5 6
| <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="sqlSessionFactory" ref="mySqlSession" /> <property name="mapperInterface" value="cn.lizhaoloveit.ssm.mapper.UserMapper" /> </bean>
|
使用注解后的做法:
1 2 3 4
| <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.lizhaoloveit.ssm.mapper"></property> </bean>
|
并且可以直接把 SQL 卸载 Mapper 接口对应的方法上,不再使用 Mapper 映射文件。
UserMapper.java
1 2
| @Select("select id, name, age from t_user") List<User> selectAll();
|
总结:
IoC 注解
作用:被贴的类,交给 Spring 管理(会被创建对象,存储在 Spring 容器中)
注解:
- Repository(DAO)
- Service(一般贴 Service 类)
- Controller(只能贴 Controller 类)
- Component(非上述所有类,默认 id 为类名首字母小写)
XML:
1 2
| <bean id="someBean" class="cn.lizhaoloveit.SomeBean" scope="singleton" init-method="open" destroy-method="close">
|
解析器:<context:component-scan basePackage="cn.lizhaoloveit.ssm">
使用注解操作如下:
1 2 3 4 5 6 7 8 9 10
| @Component("someBean") @Scope("singleton") public class SomeBean { @PostConstruct public void open() { } @PreDestroy public void close() { } }
|
DI 注解
作用:从容器中找到指定的 bean 对象,并设置给当前被贴标签的字段
注解:Autoweired、Resource 功能一样
XML:
1 2 3 4
| <bean id="otherBean" class="...OtherBean"/> <bean id="someBean" class="...SomeBean"> <property name="other" ref="otherBean"/> </bean>
|
使用注解操作如下:
解析器:<context:annotation-config/>
1 2 3 4 5 6 7 8 9
| @Component public class OtherBean {
} @Component public class SomeBean { @Autowired private OtherBean other; }
|
Tx注解
作用:让 Service 组件实现数据库事务管理操作。
注解:@Transactional
创建事务功能类对象
1 2 3
| <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" <property name="dataSource" ref="dataSource"/> </bean>
|
XML 配置
1 2 3 4 5 6 7 8 9 10
| <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
|
使用注解操作如下:
解析器:<tx:annotation-driven transaction-manager="txManager"/>
在 Service 实现类上,使用 Transactional 注解
MVC注解
XML
@Controller:声明当前类为控制器,让 Spring 容器创建对象
@RequestMapping: 贴在类和方法上,表示访问当前方法的 url (“/类url + /方法url”)
1 2 3 4 5 6 7 8
| @Controller @RequestMapping("/xxx") public class HelloController { @RequestMapping("/abc") public ModelAndView sayHello() { return null; } }
|
此时访问:localhost/xxx/abc
AOP注解
1 2 3 4 5
| public class LoginAdvice { public void writeLog() { syso("记录日志") } }
|
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
<bean id="transactionManager" class="cn.lizhaoloveit.aopjdkproxy.TransactionManager"/>
<aop:config proxy-target-class="false">
<aop:aspect ref="transactionManager"> <aop:pointcut expression="execution(* cn.lizhaoloveit.aop.service.*Service.*(..))" id="txPointcut"/> <aop:before method="begin" pointcut-ref="txPointcut"/> <aop:after-returning method="commit" pointcut-ref="txPointcut"/> <aop:after-throwing method="rollback" pointcut-ref="txPointcut" throwing="exception"/> </aop:aspect> </aop:config>
|
注解:
- Aspect 贴在增强类之上,表示一个切面 what
- Pointcut 编写切入点表达式 where
- Before 切入时机 when
- …
解析器:
<context:component-scan basePackage="...">
解析 Component
<aop:aspectj-autoproxy>
1 2 3 4 5 6 7 8 9 10 11
| @Component @Aspect public class LoginAdvice { @Pointcut("execution(* cn.lizhaoloveit.ssm.*Service.*(..))") public void Xxx() { } @Before("Xxx()") public void writeLog() { syso("日志记录"); } }
|