半注解、注解AOP、注解事务、全注解
一、半注解
use-default-filters:使用默认过滤规则,默认(true)引入所有的组件
<context:include-filter>:指定引入规则
type:规则判断依据
annotation
expression:被引入的内容的表达式
org.springframework.stereotype.Controller
<context:exclude-filter>:指定排除规则(有引入的内容)
type:规则判断依据
annotation
expression:被排除的内容的表达式
org.springframework.stereotype.Controller
案例:
配置文件
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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 测试半注解 开启注解扫描 -->
<context:component-scan base-package="com.yl"/>
<context:component-scan base-package="com.yl" use-default-filters="true">
<!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
</context:component-scan>
<!--
@Primary:设置主要的继承类,当需要注入父类类型的时候,并且父类是抽象类的时候,则默认取其主要的子类作为注入的数据
设置扫描德过滤规则
-->
</beans>
UserInfo.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 用户的实体类
*/
@Component
public class UserInfo {
private Integer userId;
@Value("张胜男")
private String userName;
private Date createTime;
private String userAddress;
private String userEmail;
public UserInfo(Integer userId, String userName, Date createTime, String userAddress, String userEmail) {
this.userId = userId;
this.userName = userName;
this.createTime = createTime;
this.userAddress = userAddress;
this.userEmail = userEmail;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
@Override
public String toString() {
return "UserInfo{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", createTime=" + createTime +
", userAddress='" + userAddress + '\'' +
", userEmail='" + userEmail + '\'' +
'}';
}
}
测试
public void testUserInfo(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
/*String[] beanNames = context.getBeanDefinitionNames();
// getBeanDefinitionNames 获取所有的
for(String beanName : beanNames){
System.out.println(beanName);
}
*/
UserInfo userInfo = context.getBean("userInfo", UserInfo.class);
System.out.println(userInfo);
System.out.println(userInfo.getPet().getClass());
}
抽象类注解案例(多个实现类):
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public abstract class Pet {
private Integer petId;
@Value("旺财")
private String petName;
private String petSex;
public Pet() {
}
public Pet(Integer petId, String petName, String petSex) {
this.petId = petId;
this.petName = petName;
this.petSex = petSex;
}
@Override
public String toString() {
return "Pet{" +
"petId=" + petId +
", petName='" + petName + '\'' +
", petSex='" + petSex + '\'' +
'}';
}
public Integer getPetId() {
return petId;
}
public void setPetId(Integer petId) {
this.petId = petId;
}
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getPetSex() {
return petSex;
}
public void setPetSex(String petSex) {
this.petSex = petSex;
}
}
cat.java 实现类
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy // 是否懒加载
public class Cat extends Pet {
private String catType;
public String getCatType() {
return catType;
}
public void setCatType(String catType) {
this.catType = catType;
}
public void zhuaLaoShu(){
System.out.println("抓老鼠!");
}
}
dog.java 实现类
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Primary // 当有多个实现类的时候,默认以该类为启动项
@Scope(value = "prototype") // 是否单列
public class Dog extends Pet {
private Integer height;
public void kanJia(){
System.out.println("狗看门!");
}
}
配置文件
<?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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 测试半注解 开启注解扫描 -->
<context:component-scan base-package="com.yl"/>
<context:component-scan base-package="com.yl" use-default-filters="true">
<!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
</context:component-scan>
<!--
@Primary:设置主要的继承类,当需要注入父类类型的时候,并且父类是抽象类的时候,则默认取其主要的子类作为注入的数据
设置扫描德过滤规则
-->
</beans>
二、注解AOP
案例:
calc.java
import org.springframework.stereotype.Component;
// 被通知类
@Component
public class Calc {
public Integer cheng(int num1,int num2){
return num1*num2;
}
public Integer chu(int num1,int num2){
return num1/num2;
}
}
MyAspect.java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
// 通知类
@Component
@Aspect
public class MyAspect {
// 切点
@Pointcut(value = "execution(* com.yl.entity.Calc.*(..))")
public void pointdemo(){}
// 前置通知
@Before("pointdemo()")
public void before(){
System.out.println("前置通知");
}
@After("pointdemo()")
public void after(JoinPoint jp){
System.out.println("后置通知-->"+ Arrays.toString(jp.getArgs()));
}
@AfterReturning(value = "pointdemo()",returning = "rv")
public void returning(Object rv){
System.out.println("返回通知-->"+rv);
}
@AfterThrowing(value = "pointdemo()",throwing = "e")
public void throwing(Exception e){
System.out.println("异常通知-->"+e.getMessage());
}
@Around("execution(* com.yl.entity.Calc.*(..))")
public Object around(ProceedingJoinPoint pjp){
System.out.println("环绕前置通知");
Object obj = null;
try {
obj = pjp.proceed();
System.out.println("环绕返回通知!");
} catch (Throwable throwable) {
System.out.println("异常通知!已将结果设置为0");
obj = 0;
}finally {
System.out.println("环绕后置通知!");
}
return obj;
}
}
测试
// 测试Aop
@Test
public void testCalc(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext01.xml");
Calc c = context.getBean( "calc",Calc.class);
Integer cheng = c.cheng(2, 5);
System.out.println(cheng);
}
配置
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.yl"/>
<!-- 开启AOP的自动代理 -->
<aop:aspectj-autoproxy/>
<!--
开启AOP的自动代理
1、打开自动代理
2、创建切面类,并交给SpringIOC容器管理,并注解@Aspect
[3、]定义切点表示式,通过@Pointcut,注解在空方法上,根据方法名()进行调用
4、定义通知方法
@Before:前置通知
@After:后置通知
@AfterReturning:返回通知
@AfterThrowing:异常通知
@Around:环绕通知
-->
</beans>
三、注解事务
开启事务的注解驱动
transaction-manager:指定当前事务驱动所使用的事务管理器
1、配置事务管理器
2、开事务的注解驱动
3、在需要使用事务的方法上打上@Transactional
propagation:传播行为
Propagation.REQUIRES_NEW:开启新事物
Propagation.REQUIRES:使用原来的事务
isolation:隔离级别
Isolation.READ_UNCOMMITTED:读未提交
Isolation.READ_COMMITTED:读已提交
Isolation.REPEATABLE_READ:可重复读
Isolation.SERIALIZABLE:序列化
timeout:事务的超时时间
read-only:事务是否只读
true:如果只读,则不会为该方法开启事务
false:会为该方法开启事务
案例
UserInfo.java
/**
*用户信息实体类
*/
public class UserInfo {
private Integer userId;
private String userName;
private String userPwd;
private Integer userBalance;
private Date userTime;
public UserInfo(Integer userId, String userName, String userPwd, Integer userBalance, Date userTime) {
this.userId = userId;
this.userName = userName;
this.userPwd = userPwd;
this.userBalance = userBalance;
this.userTime = userTime;
}
public UserInfo() {
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
public Integer getUserBalance() {
return userBalance;
}
public void setUserBalance(Integer userBalance) {
this.userBalance = userBalance;
}
public Date getUserTime() {
return userTime;
}
public void setUserTime(Date userTime) {
this.userTime = userTime;
}
@Override
public String toString() {
return "UserInfo{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userPwd='" + userPwd + '\'' +
", userBalance=" + userBalance +
", userTime=" + userTime +
'}';
}
}
UserInfoDao.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserInfoDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 修改用户余额方法
* @param userName 用户名
* @param money 钱
* @return 返回受影响行数
*/
public int updateUserBalanceByUserName(String userName,Integer money){
return jdbcTemplate.update(" UPDATE user_info SET user_balance = user_balance + ? WHERE user_name = ? ",money,userName);
}
/**
* 根据用户名查询用户余额
* @param userName 用户名
* @return 返回金额
*/
public Integer selectUserInfoBalanceByUserName(String userName){
return jdbcTemplate.queryForObject(" SELECT user_balance FROM user_info WHERE user_name = ? ",Integer.class,userName);
}
}
UserInfoService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserInfoService {
@Autowired
private UserInfoDao userInfoDao;
//事务需要具有一致性,要么都成功,要么都失败!
/**
* 转账功能
* @param un1 转入的用户
* @param un2 转出的用户
* @param money 金额
* @return 成功就返回 true
*/
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.SERIALIZABLE,readOnly = false)
public boolean zhuanZhang(String un1,String un2,Integer money){
//un1加钱
userInfoDao.updateUserBalanceByUserName(un1,money);
//判断余额
if(getUserBalanceByUserName(un2)<money){
//当余额不足,则抛出余额不足异常
throw new BalanceBuZu();
}else{
//un2减钱
userInfoDao.updateUserBalanceByUserName(un2,-money);
}
return true;
}
/**
* 根据用户账号查询余额
* @param userName 用户名
* @return 返回该用户的金额
*/
public Integer getUserBalanceByUserName(String userName){
return userInfoDao.selectUserInfoBalanceByUserName(userName);
}
}
/**
* 自定义异常
*/
class BalanceBuZu extends RuntimeException{
public BalanceBuZu() {
super("余额不足");
}
}
配置
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.yl"/>
<!-- 开启AOP自动注解 -->
<aop:aspectj-autoproxy/>
<!--事务-->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">
</property>
</bean>
<!-- 配置数据源 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///user_db?characterEncoding=UTF-8"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
测试
public void test2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext02.xml");
UserInfoService uis = ac.getBean(UserInfoService.class);
//至少两段数据库操作,一个账户减钱,一个账户加钱
boolean bl = uis.zhuanZhang("admin1","admin2",1500);
System.out.println(bl?"转账成功":"转账失败");
}
四、全注解
MySpringConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration // 让该类变成Spring的配置类
/*
* includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
)}
* @ComponentScan(value = "com.zb.beisaomiao",useDefaultFilters = true,
excludeFilters = @ComponentScan.Filter(
type=FilterType.ANNOTATION,
classes = {Controller.class, Repository.class})
)
* */
// 没有注解的方法(通过引入来注入)
@Import(value = {Test01.class, Test02.class})
public class MySpringConfiguration {
/*
* 1、注入Bean
* 手动注入
* @Bean注解,注解在方法上,SpringIOC容器会自动调用该方法,将该方法返回的对象存入到SpringIOC容器中,默认beanName使用方法名
* value/name:指定其在SpringIOC容器中的name
* autowireCandidate:是否参与自动注入
* initMethod:初始化方法
* destroyMethod:销毁方法
* 扫描注入
* @ComponentScan
* 通过引入注入
* @Import
* value:要引入的类.class数组
* ... ...
* */
//<bean name="" class="" autowire="byType/byName" />
@Bean(value ="userInfo", autowireCandidate = false,initMethod = "init",destroyMethod = "destory")
public UserInfo getUserInfo(){
UserInfo userInfo = new UserInfo();
return userInfo;
}
}
测试
import com.yl.cofiguration.MySpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestMain01 {
public static void main(String[] args) {
// 如果是通过Spring的配置类进行配置,则需要创建AnnotationConfigApplicationContext(配置类.class);
ApplicationContext ac = new AnnotationConfigApplicationContext(MySpringConfiguration.class);
String[] names = ac.getBeanDefinitionNames();
for (String name : names){
System.out.println(name);
}
}
}
五、作业
一、为工具类获取圆信息类通过SpringAOP完成代理增强
描述:
创建圆信息实现类,接口信息如下
public interface round{
public double getPerimeter(Double radius);//根据传入的半径参数,获取圆的周长
public double getArea(Double radius);//根据传入的参数半径,获取圆的面积
}
要求使用Spring的AOP功能,为该实现类添加环绕通知
通知要求:在进行计算之前验证参数是否合理(大于0)
计算之后,结果保留两位有效数(四舍五入)字并返回
Round.java
public interface Round {
//根据传入的半径参数,获取圆的周长
public double getPerimeter(Double radius);
//根据传入的参数半径,获取圆的面积
public double getArea(Double radius);
}
RoundImpl.java
import org.springframework.stereotype.Component;
// 被通知的类
@Component
public class RoundImpl implements Round{
/**
* 计算周长
* @param radius 圆的半径
* @return 返回圆的周长
*/
@Override
public double getPerimeter(Double radius) {
return Math.PI*radius*2;
}
/**
* 计算面积
* @param radius 圆的半径
* @return 返回圆的面积
*/
@Override
public double getArea(Double radius) {
return Math.PI*radius*radius;
}
}
MyAspectRound.java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
// 通知类
@Component
@Aspect
public class MyAspectRound {
// 切点
@Pointcut(value = "execution(* com.yl.calculate.RoundImpl.*(..))")
public void pointdemo(){}
// 环绕通知
@Around("pointdemo()")
public Object around(ProceedingJoinPoint pjp){
Object[] objnum = pjp.getArgs();
Object obj = null;
if((double)objnum[0]<0){
throw new ling();
}
System.out.println("您输入的半径是:"+objnum[0]+"大于 0" );
try {
obj = pjp.proceed();
} catch (Throwable throwable) {
System.out.println("异常通知!已将结果设置为0");
obj = 0;
}finally {
// 保留两位小数
BigDecimal bg = new BigDecimal((double)obj);
obj = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
return obj;
}
}
// 自定义异常
class ling extends RuntimeException{
public ling() {
super("半径小于零");
}
}
测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestRound {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Round round = ac.getBean( Round.class);
System.out.println("圆的周长为:"+round.getPerimeter(3.0));
System.out.println("圆的面积为:"+round.getArea(4.0));
}
}
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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.yl"/>
<!-- 开启AOP自动注解 -->
<aop:aspectj-autoproxy/>
</beans>
二、为服务层(BookInfoService)添加方法,并应用Spring声明式事务
public interface BookInfoService{
购买图书方法
判断图书数量是否大于等于购买数量
等于:购买成功
购买成功后,图书数量-1,用户积分+=图书单价*图书数量
不等于:购买失败,抛出异常
public boolean buyBook(String userName,int quantity);
}