StudentServiceImpl.java
package constructor; public class StudentServiceImpl implements StudentService { //依赖注入声明成员变量 private StudentDAO studentDAO; //提供构造方法 public StudentServiceImpl(StudentDAO studentDAO) { this.studentDAO = studentDAO; } @Override public void querys(String name) { System.out.println("===querys=service=="); studentDAO.querys(name); } }
spring.xml
<bean class="autodi.StudentDAOImpl" id="studentDAO"></bean> <bean class="autodi.StudentServiceImpl" id="studentService"> <constructor-arg index="0" name="studentDAO" ref="studentDAO"></constructor-arg> </bean>
测试
package constructor; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestSpring { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("constructor/spring.xml"); StudentService studentService = (StudentService) context.getBean("studentService"); studentService.querys("晓晓"); } }
注入其他属性
<!--使用构造方法注入对象--> <constructor-arg index="0" name="id" ref="user"/> <!--使用基本数据类型注入值--> <constructor-arg index="0" name="id" value="1"/> <constructor-arg index="1" name="name" value="xiaohei"/> <constructor-arg index="2" name="age" value="12"/> <constructor-arg index="3" name="qqs"> <array> <value>xxx</value> <value>222</value> <value>333</value> </array> </constructor-arg>
注意:构造注入并不常用,不过在一些框架类中必须使用构造注入,这里先了解其注入语法即可。
5.3.自动注入
自动注入对象
语法:
1.依赖:需要谁将谁声明为成员变量并提供公开的Set方法
2.注入:在配置文件中的bean标签,使用autowired属性
原理:底层使用SET方式注入
注意:只能自动完成组件对象之间的注入 不能注入八种基本类型 + String + Date + List + 数组 + Map
autowire : 为类中成员变量自动注入值
autowire=”byName”
根据注入的属性名与配置文件中bean的id匹配,一致则注入,不一致报错
autowire=”byType”
根据注入的属性类型,与配置文件中的类型匹配,类型一致注入(在多个实现类时,会产生歧义)
注意: 无论使用以上那种方式注入都需要为属性提供set方法
StudentDAOmpl.java
package autodi; public class StudentDAOImpl implements StudentDAO { @Override public void querys(String name) { System.out.println("===querys==dao="+name); } }
StudentServiceImpl.java
package autodi; public class StudentServiceImpl implements StudentService { //依赖注入声明成员变量 private StudentDAO studentDAO; //提供公开的set方法 public void setStudentDAO(StudentDAO studentDAO) { this.studentDAO = studentDAO; } @Override public void querys(String name) { System.out.println("===querys=service=="); studentDAO.querys(name); } }
spring.xml
<bean class="autodi.StudentDAOImpl" id="studentDAO"></bean> <bean class="autodi.StudentServiceImpl" id="studentService" autowire="byType"></bean>
测试
package constructor; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestSpring { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("autodi/spring.xml"); StudentService studentService = (StudentService) context.getBean("studentService"); studentService.querys("晓晓"); } }
6.Spring 的Bean工厂
6.1.bean的创建模式
singleton:单例 默认
在工厂中全局唯一,只创建一次
prototype: 多例
全局不唯一,每次使用都会创建一个新的对象
<bean id="" class="xxxx.userAction" scope="prototype|singleton"> service,dao -----> singleton struts2 action -----> prototype
注意:在项目开发中service,dao组件单例,struts2的Action必须为:多例
6.2.bean的生产原理
原理
: 反射+构造方法
UserDAOImpl userDAO = (UserDAOImpl) Class.forName("com.libin.dao.UserDAOImpl").newInstance(); System.out.println(userDAO);
6.3.bean的生命周期
何时创建
随着工厂启动,所有单例bean随之创建 非单例的bean,每次使用时创建
何时销毁
工厂**关闭,所有bean随之销毁
** ( 注意:spring对多例bean管理松散,不会负责多例bean的销毁
)
6.4.bean工厂创建对象的好处
使用配置文件管理java类,再生产环境中更换类的实现时不需要重新部署,修改文件即可
spring默认使用单例的模式创建bean,减少内存的占用
通过依赖注入建立了类与类之间的关系(使java之间关系更为清晰,方便了维护与管理)
Day2
1.现有业务层开发存在问题
a.定义业务接口
public interface UserService { void save(String name); void delete(String id); void update(); String findAll(String name); String findOne(String id); }
b.实现业务接口
public class UserServiceImpl implements UserService { @Override public void save(String name) { try { System.out.println("开启事务"); System.out.println("处理业务逻辑,调用DAO~~~"); System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } } @Override public void delete(String id) { try { System.out.println("开启事务"); System.out.println("处理业务逻辑,调用DAO~~~"); System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } } @Override public void update() { try { System.out.println("开启事务"); System.out.println("处理业务逻辑,调用DAO~~~"); System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } } @Override public String findAll(String name) { try { System.out.println("开启事务"); System.out.println("处理业务逻辑,调用DAO~~~"); System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } return name; } @Override public String findOne(String id) { try { System.out.println("开启事务"); System.out.println("处理业务逻辑,调用DAO~~~"); System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } return id; } }
问题:从上图中可以看出,现有业务层中控制事务代码出现了大量的冗余
,如何解决现有业务层出现的冗余问题?
2.代理引言
a.什么是代理
代理
: 指的是java中的一种设计模式
b.为什么需要代理
很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能。
c.代理的作用
代理对象可以在客户和目标对象之间
起到中介作用,从而为目标对象增添额外的功能
。
d.代理图例
3.静态代理的开发
目标类|对象(target)
:被代理类称之为目标类|或者被代理的对象的称之为目标对象
开发代理的原则: 代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象
a.开发静态代理类
//静态代理类 //开发原则:代理类和目标类实现相同接口,依赖于真正的目标类 public class UserServiceStaticProxy implements UserService { //真正的目标类 //target 原始业务逻辑对象 private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } @Override public void save(String name) { try { System.out.println("开启事务"); userService.save(name);//调用真正业务逻辑方法 System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } } @Override public void delete(String id) { try { System.out.println("开启事务"); userService.delete(id);//调用真正业务逻辑方法 System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } } @Override public void update() { try { System.out.println("开启事务"); userService.update();//调用真正业务逻辑方法 System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } } @Override public String findAll(String name) { try { System.out.println("开启事务"); String result = userService.findAll(name);//调用真正业务逻辑方法 System.out.println("提交事务"); return result; }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } return null; } @Override public String findOne(String id) { try { System.out.println("开启事务"); //调用目标类方法 String one = userService.findOne(id);//调用真正业务逻辑方法 System.out.println("提交事务"); return one; }catch (Exception e){ System.out.println("回滚事务"); e.printStackTrace(); } return null; } }
b.更改目标实现类
public class UserServiceImpl implements UserService { @Override public void save(String name) { System.out.println("处理业务逻辑,调用DAO~~~"); } @Override public void delete(String id) { System.out.println("处理业务逻辑,调用DAO~~~"); } @Override public void update() { System.out.println("处理业务逻辑,调用DAO~~~"); } @Override public String findAll(String name) { System.out.println("处理业务逻辑,调用DAO~~~"); return name; } @Override public String findOne(String id) { System.out.println("处理业务逻辑,调用DAO~~~"); return id; } }
c.配置静态代理类
<!--配置目标类--> <bean id="userService" class="staticproxy.UserServiceImpl"></bean> <!--配置代理类--> <bean id="userServiceStaticProxy" class="staticproxy.UserServiceStaticProxy"> <!--注入目标对象--> <property name="userService" ref="userService"/> </bean>
d.调用代理方法
ApplicationContext c
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); UserService userServiceStaticProxy = (UserService) context.getBean("userServiceStaticProxy"); userServiceStaticProxy.save("晓晓");
新的问题:往往在开发我们书写的不仅仅是一个业务层,两个业务层,而我们的业务层会有很多,如果为每一个业务层开发一个静态代理类,不仅没有减轻工作量,甚至让我们的工作量多了一倍不止怎么解决以上这个问题呢?
解决方案: 为业务层在运行过程中动态创建代理类,通过动态代理类去解决我们现有业务层中业务代码冗余的问题 .
4.动态代理的原理
通过jdk提供的Proxy这个类,动态为现有的业务生成代理类
参数一:当前线程类加载器
参数二:生成代理类的接口类型
参数三:通过代理类对象调用方法时会优先进入参数三中的invoke方Proxy.newProxyInstance(loader, interfaces, h);//返回值就是动态代理对象
public class TestDynamicProxy { public static void main(String[] args) { final UserService userService = new UserServiceImpl(); //参数1:当前线程类加载器 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); //参数2: Class[] classes = new Class[]{UserService.class}; //参数3: UserService userServiceProxy = (UserService) Proxy.newProxyInstance(contextClassLoader, classes, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try{ System.out.println("开启事务");//附加操作 Object invoke = method.invoke(userService, args); System.out.println("提交事务");//附加操作 return invoke; }catch (Exception e){ System.out.println("回滚事务");//附加操作 } return null; } }); userServiceProxy.save("小晓"); } }
5.AOP (Aspect Oriented Programming)编程
通知(Advice)
:除了目标方法以外的操作都称之为通知
切入点(PointCut): 要为哪些类中的哪些方法加入通知
切面(Aspect)
:通知 + 切入点
1.通知分类
2.编程步骤
# 1.引入依赖 spring-aop spring-expression spring-aspects # 2.开发通知类 MethodBeforeAdvice 前置通知 MethodInterceptor 环绕通知 AfterReturningAdvice 返回后通知 ThrowsAdvice 异常通知 MyAdvice implements 通知接口{.....} //自定义通知类:用来完成额外功能 public class MyAdvice implements MethodBeforeAdvice { @Override//参数1:当前调用的方法对象 //参数2:当前调用方法对象的参数 //参数3:目标对象 public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("目标方法名: "+method.getName()); System.out.println("目标方法的参数: "+objects); System.out.println("目标对象: "+o.getClass()); } } # 3.配置切面 a.引入aop命名空间 <?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" 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"> b.管理通知 <!--管理通知类--> <bean id="myAdvice" class="before.MyAdvice"/> c.配置切面 <aop:config> <aop:pointcut id="pc" expression="execution(* before.UserServiceImpl.*(..))"/> <aop:advisor advice-ref="myAdvice" pointcut-ref="pc"/> </aop:config> # 4.启动工厂测试 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("before/spring.xml"); UserService userSerivce = (UserService) context.getBean("userService"); System.out.println(userSerivce.getClass()); userSerivce.save("晓晓");
3.前置通知的使用
4.环绕通知的使用
5.返回后通知
6.异常通知
6.切入点表达表
1.execution方法级别的切入点表达式
注意:方法级别的切入点表达式尽可能精准,否则程序运行可能出现异常
2.within类级别的切入点表达式
# 1.语法 within(包.类) # 2.示例 within(com. service.*) 包: com. service 类: 所有类中所有方法不关心返回值和参数 within(com. service.UserServiceImpl) 包: com.libin.service 类: UserServiceImpl类中所有方法不关心返回值和参数
注意:within的效率高于execution表达式,推荐使用within表达式
如果大家觉得还不错,点赞,收藏,分享,一键三连支持我一下~