Spring架构由诸多模块组成,可分类为java
Spring架构组成以下图web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.qf</groupId> <artifactId>hello-spring</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- Spring经常使用依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> </dependencies> </project>
命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
public class MyClass{ public void show(){ System.out.println("HelloWorld"); } }
在spring-context.xml中配置MyClass的bean后,当项目启动时,spring容器会自动建立MyClass实例,这个实例名字叫mcspring
<!-- 配置实例(id:“惟一标识” class="须要被建立的目标对象全限定名") --> <bean id="mc" class="com.qf.spring.part1.factory.MyClass" />
测试代码express
public class TestFactory{ /** * 程序中的对象都交由Spring的ApplicationContext工厂进行建立。 */ public static void main(String[] args){ //1\. 读取配置文件中所需建立的bean对象,并得到工厂对象 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml"); //2\. 经过id获取bean对象 MyClass mc = (MyClass) ctx.getBean("mc"); //3\. 使用对象 mc.show(); } }
控制反转是Spring框架的核心,所谓控制反转就是应用自己不负责依赖对象的建立及维护,依赖对象的建立及维护是由外部容器负责的, 这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。这样就由以前的本身建立依赖对象,变为由spring容器建立。(变主动为被动,即反转)。控制反转解决了具备依赖关系的组件之间的强耦合,使得项目形态更加稳健。apache
public class UserDAOImpl implements UserDAO{....}
public class UserServiceImpl implements UserService { // 经过传统的new方式强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不稳健!! private UserDAO userDAO= new UserDAOImpl(); @Override public User queryUser() { return userDAO.queryUser(); } .... }
// 不引用任何一个具体的组件(实现类),在须要其余组件的位置预留存取值入口(set/get) public class UserServiceImpl implements UserService { // !!!再也不耦合任何DAO实现!!!,消除不稳健因素!! private UserDAO userDAO; // 为userDAO定义set/get,容许userDAO属性接收spring赋值 //Getters And Setters @Override public User queryUser() { return userDAO.queryUser(); } .... }
在spring配置文件中配置UserDAO和UserService对应的bean编程
<bean id="userDAO" class="com.qf.spring.part1.injection.UserDaoImpl"></bean> <!-- UserServiceImpl组件 --> <bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl"> <!-- 由spring为userDAO属性赋值,值为id="userDAO"的bean --> <property name="userDAO" ref="userDAO"/> </bean>
在Spring建立对象的同时,为其属性赋值,称之为依赖注入,注入方式主要有如下2种设计模式
建立对象时,Spring工厂会经过Setter方法为对象的属性赋值。缓存
public class User { private Integer id; private String password; private String sex; private Integer age; private Date bornDate; private String[] hobbys; private Set<String> phones; private List<String> names; private Map<String,String> countries; private Properties files; //Getters And Setters }
<bean id="u1" class="com.qf.spring.part1.injection.User"> <!--base field--> <property name="id" value="1001" /> <property name="password" value="123456" /> <property name="sex" value="male" /> <property name="age" value="20" /> <property name="bornDate" value="1990/1/1" /><!--注意格式"/"--> </bean>
<bean id="u1" class="com.qf.spring.part1.injection.User"> <!--Array--> <property name="hobbys"> <array> <value>Run</value> <value>Swim</value> <value>Climb</value> </array> </property> <!--Set--> <property name="phones"> <set> <value>13777777777</value> <value>13888888888</value> <value>13999999999</value> </set> </property> <!--List--> <property name="names"> <list> <value>tom</value> <value>jack</value> <value>marry</value> </list> </property> <!--Map--> <property name="countries"> <map> <entry key="CN" value="China" /> <entry key="US" value="America" /> <entry key="KR" value="Korea" /> </map> </property> <!--Properties--> <property name="files"> <props> <prop key="first">One</prop> <prop key="second">Two</prop> <prop key="third">Three</prop> </props> </property> </bean>
<!--次要bean,被做为属性--> <bean id="addr" class="com.qf.spring.part1.injection.Address"> <property name="position" value="北京市海淀区" /> <property name="zipCode" value="100001" /> </bean> <!--主要bean,操做的主体--> <bean id="u2" class="com.qf.spring.part1.injection.User"> <property name="address" ref="addr" /><!--address属性引用addr对象--> </bean>
建立对象时,Spring工厂会经过构造方法为对象的属性赋值。服务器
public class Student { private Integer id; private String name; private String sex; private Integer age; //Constructors public Student(Integer id , String name , String sex , Integer age){ this.id = id; this.name = name; this.sex = sex; this.age = age; } }
<!--构造注入--> <bean id="u3" class="com.qf.zcg.spring.day1.t2.ioc.Student"> <constructor-arg name="id" value="1234" /> <!-- 除标签名称有变化,其余均和Set注入一致 --> <constructor-arg name="name" value="tom" /> <constructor-arg name="age" value="20" /> <constructor-arg name="sex" value="male" /> </bean>
工厂建立以后,会将Spring配置文件中的全部对象都建立完成(饿汉式),提升程序运行效率,避免屡次IO,减小对象建立时间。(概念接近链接池,一次性建立好,使用时直接获取)
import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @PostConstruct //初始化 public void init(){ System.out.println("init method executed"); } @PreDestroy //销毁 public void destroy(){ System.out.println("destroy method executed"); }
单例bean:singleton
随工厂启动建立 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》随工厂关闭销毁
多例bean:prototype
被使用时建立 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》JVM垃圾回收销毁
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。
经过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。
//目标 final OrderService os = new OrderServiceImpl(); //额外功能 InvocationHandler handler = new InvocationHandler(){//1.设置回调函数(额外功能代码) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start..."); method.invoke(os, args); System.out.println("end..."); return null; } }; //2.建立动态代理类 Object proxyObj = Proxy.newProxyInstance(ClassLoader , Interfaces , InvocationHandler);
final OrderService os = new OrderServiceImpl(); Enhancer cnh = new Enhancer();//1.建立字节码曾强对象 enh.setSuperclass(os.getClass());//2.设置父类(等价于实现原始类接口) enh.setCallback(new InvocationHandler(){//3.设置回调函数(额外功能代码) @Override public Object invoke(Object proxy , Method method, Object[] args) throws Throwable{ System.out.println("start..."); Object ret = method.invoke(os,args); System.out.println("end..."); return ret; } }); OrderService proxy = (OrderService)enh.create();//4.建立动态代理类 proxy,createOrder();
AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减小系统的重复代码,下降模块之间的耦合度,并有利于将来的可操做性和可维护性。
通俗的概念来说,所谓的面向切面编程就是针对被代理对象的方法在某个特定的执行时机(方法调用以前、方法调用以后、方法抛出异常),作出一些额外的横向的处理。好处在于:1. 能够在不修改原有代码基础上横向扩展咱们的内容;2. 将一些方法中的通用逻辑进行统一化的处理。
OOP(面向对象编程)和AOP(面向切面编程)的区别:oop是对类纵向的扩展;aop是横向的扩展。
Spring的AOP编程便是经过动态代理类为原始类的方法添加辅助功能。
引入AOP相关依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.6.RELEASE</version> </dependency>
spring-context.xml引入AOP命名空间
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 "> </beans>
定义原始类
package com.qf.aaron.aop.basic; public interface UserService { public void save(); }
package com.qf.aaron.aop.basic; public class UserServiceImpl implements UserService { public void save() { System.out.println("save method executed..."); } }
定义通知类(添加额外功能
package com.qf.aaron.aop.basic; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyAdvice implements MethodBeforeAdvice { //实现前置通知接口 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("before advice executed..."); } }
定义bean标签
<!--原始对象--> <bean id="us" class="com.qf.aaron.aop.basic.UserServiceImpl" /> <!--辅助对象--> <bean id="myAdvice" class="com.qf.aaron.aop.basic.MyAdvice" />
定义切入点(PointCut)
造成切面(Aspect)
<aop:config> <!--切点--> <aop:pointcut id="myPointCut" expression="execution(* save())" /> </aop:config> ```java <aop:config> <!--组装切面 --> <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut" /> </aop:config>
可定义的通知类有6种,能够按需求选择通知类。
前置通知:MethodBeforeAdvice 后置通知:AfterAdvice 后置通知:AfterReturningAdvice //有异常不执行,方法会因异常而结束,无返回值 异常通知:ThrowsAdvice 环绕通知:MethodInterceptor
可是spring中默认开启JDK动态代理,当须要使用CGLIB动态代理时,须要在spring配置文件中配置。
<!-- 使用cglib的方式实现aop --> <aop:aspectj-autoproxy proxy-target-class="true"/>
编写aspectJ通知代码:
@Aspect public class AspectJAdvisor { // 环绕通知 @Around("execution(* org.example.service.impl.*.*(..))") public Object timer(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("前置通知"); Object o = pjp.proceed(); System.out.println("【后置通知】"); return o; } // 后置通知 @After("execution(* org.example.service.impl.*.*(..))") public void after(JoinPoint jp) throws Throwable{ System.out.println("====After method invokded===="); } // 前置通知 @Before("execution(* org.example.service.impl.*.*(..))") public void before(JoinPoint jp){ System.out.println("====before method invoked===="); } // 正常返回的通知 @AfterReturning("execution(* org.example.service.impl.*.*(..))") public void afterReturning(JoinPoint jp){ System.out.println("====after value return===="); } // 抛出异常后的通知,方法的异常必须与代理类抛出的异常一致,throwing的值要与异常的形参名保持一致 @AfterThrowing(value = "execution(* org.example.service.impl.*.*(..))", throwing="npe") public void afterThrowException(JoinPoint jp, NullPointerException npe){ System.out.println("====after exception throwing===="); } }
配置
<bean id="userService" class="org.example.service.impl.UserServiceImpl"></bean> <bean id="throwsAdvisor" class="org.example.advisor.AspectJAdvisor"></bean> <!--- aspectJ是使用cglib来实现动态代理的 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
用于替换自建类型组件的 <bean…>标签;能够更快速的声明bean
// @Service说明 此类是一个业务类,须要将此类归入工厂 等价替换掉 <bean class="xxx.UserServiceImpl"> // @Service默认beanId == 首字母小写的类名"userServiceImpl" // @Service("userService") 自定义beanId为"userService" @Service //声明bean,且id="userServiceImpl" @Scope("singleton") //声明建立模式,默认为单例模式 ;@Scope("prototype")便可设置为多例模式 public class UserServiceImpl implements UserService { ... }
用于完成bean中属性值的注入
@Service public class UserServiceImpl implements UserService { @Autowired //注入类型为UserDAO的bean @Qualifier("userDAO2") //若是有多个类型为UserDAO的bean,能够用此注解从中挑选一个 private UserDAO userDAO; }
@Service public class UserServiceImpl implements UserService { @Resource("userDAO3") //注入id=“userDAO3”的bean private UserDAO userDAO; /* @Resource //注入id=“userDAO”的bean private UserDAO userDAO; */ }
public class XX{ @Value("100") //注入数字 private Integer id; @Value("shine") //注入String private String name; }
用于控制事务切入
//类中的每一个方法都切入事务(有本身的事务控制的方法除外) @Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class,timeout = -1) public class UserServiceImpl implements UserService { ... //该方法本身的事务控制,仅对此方法有效 @Transactional(propagation=Propagation.SUPPORTS) public List<User> queryAll() { return userDao.queryAll(); } public void save(User user){ userDao.save(user); } }
<!-- 告知spring,哪些包中 有被注解的类、方法、属性 --> <!-- <context:component-scan base-package="com.qf.a,com.xx.b"></context:component-scan> --> <context:component-scan base-package="com.qf"></context:component-scan> <!-- 告知spring,@Transactional在定制事务时,基于txManager=DataSourceTransactionManager --> <tx:annotation-driven transaction-manager="txManager"/>
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice) @Component //声明组件,进入工厂 public class MyAspect { // 定义切入点 @Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))") public void pc(){} @Before("pc()") // 前置通知 public void mybefore(JoinPoint a) { System.out.println("target:"+a.getTarget()); System.out.println("args:"+a.getArgs()); System.out.println("method's name:"+a.getSignature().getName()); System.out.println("before~~~~"); } @AfterReturning(value="pc()",returning="ret") // 后置通知 public void myAfterReturning(JoinPoint a,Object ret){ System.out.println("after~~~~:"+ret); } @Around("pc()") // 环绕通知 public Object myInterceptor(ProceedingJoinPoint p) throws Throwable { System.out.println("interceptor1~~~~"); Object ret = p.proceed(); System.out.println("interceptor2~~~~"); return ret; } @AfterThrowing(value="pc()",throwing="ex") // 异常通知 public void myThrows(JoinPoint jp,Exception ex){ System.out.println("throws"); System.out.println("===="+ex.getMessage()); } }
<!-- 添加以下配置,启用aop注解 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.6.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
能够免去工厂的建立过程;
能够直接将要测试的组件注入到测试类。
@RunWith(SpringJUnit4Cla***unner.class) //由SpringJUnit4Cla***unner启动测试 @ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件位置 public class SpringTest{//当前测试类也会被归入工厂中,因此其中属性能够注入 @Autowired // 注入要测试的组件 @Qualifier("userDAO") private UserDAO userDAO; @Test public void test(){ // 测试使用userDAO userDAO.queryUser(); .... } }
感谢你看到这里,看完有什么的不懂的能够在评论区问我,以为文章对你有帮助的话记得给我点个赞,天天都会分享java相关技术文章或行业资讯,欢迎你们关注和转发文章!