Spring IoC和AOP的扩展
IOC解决了对象的创建 控制反转 不需要再写实现类
DI属性的赋值 依赖注入
aop 面向切面编程
IOC理解
对象的生命周期管理交给了Spring,对象的属性的注入(DI)也交给Spring来管理。(IoC容器/Spring容器 -> 面向bean编程)
AOP理解
在不改变原有代码的基础上,采用代理机制动态在程序运行过程中添加一系列的功能(将公共的功能集中到一起)。
1. 掌握不同的依赖注入方式(DI-IoC)
1.1 掌握设值注入(最重要)-不同数据类型
设值注入,这是我们在Spring应用最多且最广的一种依赖注入方式,非常符合我们以前的开发习惯。基于setter方法来实现依赖注入。(setter强烈推荐你别自己写,让工具帮你生成!)
<!-- User -->
<bean id="user" class="cn.kgc.demo1.User">
<property name="username" value="黑色"></property>
</bean>
<bean id="testEntity" class="cn.bdqn.demo2.TestEntity">
<!-- TestEntity bean组件 -->
设值注入applicationContext.xml里面的
在xml中不能出现特殊符号的 >
<!-- 特殊字符值1 -->
<property name="specialCharacter1" value="1>2"></property>
<!-- 特殊字符值2 -->
<property name="specialCharacter2">
<value><![CDATA[1>2]]></value>
</property>
都是1>2
<!--对象类型 方式1外部bean引入-->
<property name="innerBean" ref="user"></property>
<!--对象类型 方式2 内部bean -->
<property name="innerBean">
<!--不用在加id 内部bean只能用一次-->
<bean class="cn.kgc.demo1.User">
<property name="username" value="黑色"></property>
</bean>
</property>
外部bean写了id说明他可以被重复用
<!--集合类型 -->
<property name="sportList">
<list>
<value>游泳</value>
<value>下棋</value>
</list>
</property>
<!--数组类型 -->
<property name="array">
<array>
<value>下棋</value>
<value>游泳</value>
</array>
</property>
<!-- set集合 -->
<property name="set">
<set>
<value>唱</value>
<value>跳</value>
</set>
</property>
<!--map集合 未来多个键值对 就写多个entrty-->
<property name="map">
<map>
<entry>
<key>
<value>CN</value>
</key>
<value>中国</value>
</entry>
</map>
</property>
properties HashTable里面的一个子类
<!-- properties类型-->
<property name="props">
<props>
<prop key="Us">美国</prop>
</props>
</property>
<!-- 空字符串1 -->
<property name="emptyValue" value=""></property>
<!-- 空字符串2 -->
<!-- <property name="emptyValue">
<value></value>
</property>
-->
<!--空值 -->
<!-- 这个输出的是字符串的null 不对
<property name="nullValue" value="null"></property>
-->
<property name="nullValue">
<null></null>或直接写成<null/>
</property>
</bean>
注入空字符串:[]
注入null值:null
public class TestEntity {
private String specialCharacter1; // 特殊字符值1
private String specialCharacter2; // 特殊字符值2
private User innerBean; // JavaBean类型
private List<String> sportList; // List类型
private String[] array; // 数组类型
private Set<String> set; // Set类型
private Map<String, String> map; // Map类型
private Properties props; // Properties类型
private String emptyValue; // 注入空字符串值
private String nullValue = "init value"; // 注入null值
自己加getset方法
1.2 掌握构造注入
以前我们测试他的时候 是提供一下get+set方法 用的是设值注入
现在 写个构造(建议你把无参也写出来)
基于构造方法(带参构造)实现注入!
之前的设值注入使用的是无参构造(默认调用)。
代码块一:
public class UserService {
private String username;
private User user;
private int age;
// 兼容类型的冲突问题 例如:12既可以存储为字符串 也可以存储为数值
// 出现兼容性问题时 它会按照构造参数上的顺序进行赋值
public UserService(User user,String username,int age) {
this.user = user;
this.username = username;
this.age = age;
}
public UserService() {
}
public void test() {
System.out.println("用户名为:"+username);
System.out.println("用户年龄为:"+age);
System.out.println("User的用户名为:"+user.getUsername());
}
}
<bean id="userService" class="cn.kgc.demo2.UserService">
<constructor-arg value="12" name="age"/>
<constructor-arg value="小明"/>
<constructor-arg ref="user"/>
</bean>
<bean id="user" class="cn.kgc.demo2.User">
<property name="username" value="小明"></property>
</bean>
代码块二:
public class UserService {
private String username;
// 单个参数构造方法
public UserService(String username) {
this.username = username;
}
public UserService() {}
public void test() {
System.out.println("用户名为:"+username);
}
}
构造注入:单个参数构造方法
applicationContext.xml
<bean id="userService" class="demo2.UserService">
<constructor-arg value="小明"/>
</bean>
代码块三:
public class UserService {
private String username;
private User user;
private int age;
// 多个不同类型参数的构造方法
public UserService(User user,String username) {
this.user = user;
this.username = username;
}
public UserService() {
}
public void test() {
System.out.println("用户名为:"+username);
//System.out.println("用户年龄为:"+age);
System.out.println("User的用户名为:"+user.getUsername());
}
}
构造注入:多个不同类型参数的构造方法
<bean id="userService" class="demo2.UserService">
<constructor-arg value="username的值为黑色"/>
<constructor-arg ref="user"/>
</bean>
<bean id="user" class="demo2.User">
<property name="username" value="小明"></property>
</bean>
代码块四:
package demo2;
public class UserService {
private String username;
private User user;
private int age;
public UserService(String username,int age) {
this.username = username;
this.age = age;
}
public UserService() { }
public void test() {
System.out.println("用户名为:"+username);
System.out.println("用户年龄为:"+age);
}
}
<!-- 下面的index type name属性了解即可 -->
<!-- <constructor-arg value="12" index="2"/> -->
<!-- <constructor-arg value="12" type="int"/> -->
<!-- 带参构造方法注入 -->
<!--
1.如果构造方法的参数都是不同类型,那么在进行编写<constructor-arg>给参数赋值时,不需要区分顺序。
2.如果构造方法的参数有相同类型的或者不同类型但是却可以赋值的,那么它会按照你赋值的顺序依次给其参数注值。
例如: 参数int 11
参数String 11
2.1 我们可以使用index属性 控制值的注入位置 index索引(在构造方法中的参数的索引)从0开始
2.2 我们可以使用type属性 控制值的注入位置 int 可以接受123这种值 String也可以接受123这种值 所以你可以进行区分。
-->
<bean id="userService" class="demo2.UserService">
<constructor-arg value="黑色" index="0"/>
<constructor-arg value="12" index="1"/>
</bean>
1.3 掌握p命名空间注入
p命名空间注入它是基于设值注入,写法上简洁一些。内部bean 太麻烦 优化 p命名空间注入
xmlns:p="http://www.springframework.org/schema/p"
以前:<bean id="userService" class="cn.kgc.demo3.UserService">
<property name="userName" value="黑色UserService"></property>
<property name="User">
<bean class="cn.kgc.demo3.User">
<property name="username" value="黑色User"></property>
</bean>
</property>
</bean>
现在:
对于直接量(基本数据类型 字符串) p:属性名="属性值"
对于引用Bean(对象)的属性 p:属性名-ref="Bean的id"
<bean id="userService" class="cn.kgc.demo3.UserService"
p:userName="黑色UserService" p:user-ref="user"/>
<bean id="user" class="cn.kgc.demo3.User">
<property name="username" value="黑色User"></property>
</bean>
2. 掌握更多的增强处理的类型(AOP)
异常类配置:
<!--将我们自己配置的增强处理类 织入到指定的方法上(切点表达式) -->
<bean id="随便起1 保证唯一" class="增强类全类名" />
<aop:config>
<!--配置切点选择器:选择匹配的方法 id唯一 -->
<aop:pointcut
expression="execution(表达式)" id="随便起2 保证唯一" />
<!-- 配置切面 引入增强处理类 配置增强处理方法 -->
<aop:aspect ref="随便起1">
<aop:before method="前置增强类方法名" pointcut-ref="随便起2" />
<aop:after-returning method="后置增强类方法名" returning="返回值名" pointcut-ref="随便起2" />
<aop:after-throwing method="异常抛出增强方法名" throwing="抛出异常的名" pointcut-ref="随便起2" />
<!--最终增强-->
<aop:after method="afterMethod" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
前置增强
后置增强
异常抛出增强:(出现异常的话 后置增强就不会执行)
// 异常抛出增强 当异常出现之后 会执行的增强
public void throwsExceptionMethod(JoinPoint jp,Exception e) {
Signature signature = jp.getSignature();
// 获取方法名
String methodName = signature.getName();
logger.error("<=="+methodName+"方法执行出现异常<==异常信息为:"+e);
}
pointcut-ref="logPointCut" 表示引用上面的切面规则
还有一个poingcut属性 如果你不想引用上面的匹配规则 就自己写
在配置切面的时候配置新的匹配规则(还是和上面的expression 里面的一样)
此处省略掉部分代码
<aop:after-throwing method="throwsExceptionMethod" throwing="e" pointcut-ref="logPointCut"/>
最终增强
// 最终增强 finally执行内容
public void afterMethod(JoinPoint jp) {
Signature signature = jp.getSignature();
// 获取方法名
String methodName = signature.getName();
logger.error("<=="+methodName+"方法正在执行最终处理内容");
}
<aop:after method="afterMethod" pointcut-ref="logPointCut"/>
环绕增强:上述所有的增强整合
// 环绕增强
public Object aroundMethod(ProceedingJoinPoint jp) {
Signature signature = jp.getSignature();
String methodName = signature.getName();
String argStr = Arrays.toString(jp.getArgs());
// 前置增强
logger.info("==>正在执行"+methodName+"方法==>方法参数为:"+argStr);
// 此返回值要对应上对应的方法的返回值类型
Object returnResult = null;
try {
// 执行目标方法
returnResult = jp.proceed();
// 后置增强
logger.info("<=="+methodName+"方法执行结束<==方法返回值为:"+returnResult);
} catch (Throwable e) {
e.printStackTrace();
// 异常抛出增强
logger.error("<=="+methodName+"方法执行出现异常<==异常信息为:"+e);
}finally {
// 最终增强
logger.error("<=="+methodName+"方法正在执行最终处理内容");
}
return returnResult;
}
<aop:around method="aroundMethod" pointcut-ref="logPointCut"/>
3. 掌握注解实现IoC和AOP
XML(更接近于基础实现) -> XML + 注解 -> 纯注解
有了spring以后就不需要写实现类了 因为我们采用了ioc注入
创建bean以后 就会帮助我们来创建对象了
以前我们是这样配置对象 但是我们如果有更多的dao 配置起来比较麻烦 class里面写的是具体的类信息 引入了注解
<bean id="userDao" class="cn.kgc.demo5.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="cn.kgc.demo5.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
package dao;
import demo1.User;
public interface UserDao {
int add(User user)throws Exception;
}
package dao;
import demo1.User;
public class UserDaoImpl implements UserDao{
public int add(User user) throws Exception {
System.out.println("用户添加成功");
return 1;
}
}
package service;
import dao.UserDao;
import demo1.User;
public interface UserService {
public boolean add(User user)throws Exception;
}
(1)未使用注解
package service;
import org.springframework.beans.factory.annotation.Autowired;
import dao.UserDao;
import demo1.User;
public class UserServiceImpl implements UserService{
private UserDao userDao;
public boolean add(User user) throws Exception {
boolean flag=false;
if(userDao.add(user)>0) {
return true;
}
return flag;
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
package demo1;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
public class Test{
public static void main(String[] args) throws Exception {
// 1.加载核心配置文件
AbstractXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.获取对象 IOC DI 你想获取谁的对象 就使用对应的配置文件中配置的id来获取即可
UserService userService=(UserService) ac.getBean("userService","UserServie.class");
User user = new User("heise1");
boolean flag = userService.add(user);
if(flag) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="userDao" class="dao.UserDaoImpl"></bean>
<bean id="userService" class="service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
(2)使用注解autowired
package service;
import org.springframework.beans.factory.annotation.Autowired;
import dao.UserDao;
import demo1.User;
public class UserServiceImpl implements UserService{
//使用注解@Autowired 就不需要加getset方法 配置文件中 property name="userDao" ref="userDao"这个就不要写了,写了反而报错 ,同时配置文件中还要加component-scan
@Autowired
private UserDao userDao;
public boolean add(User user) throws Exception {
boolean flag=false;
if(userDao.add(user)>0) {
return true;
}
return flag;
}
/* public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
*/
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="userDao" class="dao.UserDaoImpl"></bean>
<bean id="userService" class="service.UserServiceImpl">
<!-- <property name="userDao" ref="userDao"></property> -->
</bean>
<!-- 扫描包中注解标注的类 -->
<context:component-scan base-package="service"></context:component-scan>
</beans>
3.1 实现IoC
以下注解能帮你实现自动生成bean:(IoC)
@Controller 表现层
@Service 业务逻辑层
@Repository 数据访问层 仓库 数据存储上 dao层
@Component 组件/部件(适合于非三层架构的类) 通用的
//<bean id="userDao" class="cn.kgc.demo5.dao.impl.UserDaoImpl"></bean>
//注意小写("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
// 省略部分代码
}
//<bean id="userService" class="cn.kgc.demo5.service.impl.UserServiceImpl">
//注意小写("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
//@Autowired //自动装配注入 按照名字和类型查找 名字不一样就按照类型找 也防止了侵入
@Resource //按照名字和类型查找 @Resource(name="")按照指定的名字进行查找
private UserDao userDao;
在 applicationContext.xml配置一下Context
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
base-package指定扫描包 自动扫描包下的所有内容 包括子包下的
扫描的如果还有别的包 逗号隔开
扫描所有的包 cn.kgc
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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="cn.kgc.demo5"></context:component-scan>
</beans>
以下注解可以实现自动依赖注入:(DI)
@Autowired 适合于对象属性注入 它和Resource都可以实现按照名称和类型来注入(自动查找)。 位于org.springframework.beans.factory.annotation.Autowired包中 不需要你写set方法 根据你的业务判断你的get set方法还要不要
@Resource 适合于对象属性注入 javax.annotation.Resource;
@Value 适合于普通值注入
增删改的事务注解transactional
@Resource装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
3.2 实现AOP
@Component // <bean id="loggerIncreseClass" class="cn.kgc.demo4.increse.LoggerIncreseClass"/>
@Aspect // <aop:aspect ref="loggerIncreseClass">
public class LoggerIncreseClass {
private Logger logger = Logger.getLogger(LoggerIncreseClass.class);
/*使用注解 未来如果多个切点一样 我们还要配置多个 所以我们可以写一个方法*/
@Pointcut("execution(* cn.kgc.demo6.UserService.*(..))")
public void pointCut() {};
// 环绕增强
// @Around("execution(* cn.kgc.demo6.UserService.*(..))")
@Around("pointCut()")
public Object aroundMethod(ProceedingJoinPoint jp) {
Signature signature = jp.getSignature();
String methodName = signature.getName();
String argStr = Arrays.toString(jp.getArgs());
// 前置增强
logger.info("==>正在执行"+methodName+"方法==>方法参数为:"+argStr);
// 此返回值要对应上对应的方法的返回值类型
Object returnResult = null;
try {
// 执行目标方法
returnResult = jp.proceed();
// 后置增强
logger.info("<=="+methodName+"方法执行结束<==方法返回值为:"+returnResult);
} catch (Throwable e) {
e.printStackTrace();
// 异常抛出增强
logger.error("<=="+methodName+"方法执行出现异常<==异常信息为:"+e);
}finally {
// 最终增强
logger.error("<=="+methodName+"方法正在执行最终处理内容");
}
return returnResult;
}
}
<!--启用Ioc注解扫描-->
<context:component-scan base-package="cn.kgc.demo6"></context:component-scan>
<!-- 启用AOP注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>