人物:Rod Johson。。。 interface21
spring是一个开源的免费的框架(容器)! spring是一个轻量级的,非入侵式的框架! 控制反转(IOC) 面向切面编程(AOP)! 支持事务的处理,对框架整合的支持! 合一:spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
又:控制反转。众所周知的实现方式:DI(dependency injection)依赖注入
思想转变为,增加一个该实现类 类型的成员变量,并加上setter方法。 在客户端处理时,设置真实要用的那个实现类。 这话听着云里雾里的。。。。。 先看下某条业务线的常规结构 看下代码示例: 陈旧的方式:
public interface UserDao {//业务接口 void getUser(); } //********************************** public class UserDaoImpl implements UserDao{//业务实现类1 public void getUser() { System.out.println("获取用户数据。。。"); } } //*********天降需求后********** public class UserDaoMysqlImpl implements UserDao {//业务实现类2 public void getUser() { System.out.println("从mysql获取数据!"); } } //*********** 还可以有n多个 **************** public interface UserService {//服务层接口 void getUser(); } //********************************** public class UserServiceImpl implements UserService {//服务层实现类 // private UserDao userDao = new UserDaoImpl();//满足业务1 private UserDao userDao = new UserDaoMysqlImpl ();//满足业务2 public void getUser() { userDao.getUser(); } } //********************************** public class NyTest { @Test public void Test() {//模拟终端调用 UserServiceImpl userService = (UserServiceImpl) new UserServiceImpl(); userService.getUser(); } }这样每次新增实现,都会疯狂改服务层实现,违背了,开发新业务不要影响原有业务代码的原则!
思想转变后的方式:(主要关注服务层)
import com.kuang.dao.UserDao; public class UserServiceImpl implements UserService { private UserDao userDao;//老式是写死的 //set方式注入 控制权 交由最终调用侧 新增实现后就不需要更改业务层代码了 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } } //********************************** public class NyTest { @Test public void Test() {//模拟终端调用 UserService userService = new UserServiceImpl(); // userService.setUserDao(new UserDaoOracleImpl()); //增加业务,只需要在终端侧改变传入的实现即可 控制反转就是体现在这里,new的地方变了!!! userService.setUserDao(new UserDaoMysqlImpl()); userService.getUser(); } }有了这个思想基础后,就是铺天盖地的各种花式花样的注入方式 例如盛行一时,甚至至今仍然强势的一种 beans.xml配置文件注入
1关于构造器的有无参数 实体类举例 package com.kuang.pojo; public class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void show() { System.out.println(this.name + "说Java"); } } //*************************************** package com.kuang.pojo; public class UserT { private String name; public UserT() { System.out.println("UserT被初始化了!!!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }相应注入方式的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"> <!--默认使用无参构造--> <bean id="uerT" class="com.kuang.pojo.UserT" name="a b,c;d t2"/> <!--有参构造 注入方式1--> <!--<bean id="user" class="com.kuang.pojo.User">--> <!--<constructor-arg index="0" value="小狂神"/>--> <!--</bean>--> <!--有参构造 注入方式2 不推荐,当有2个同类型参数时,不好处理--> <!--<bean id="user" class="com.kuang.pojo.User">--> <!--<constructor-arg type="java.lang.String" value="小狂神1号"/>--> <!--</bean>--> <!--有参构造 注入方式3 推荐使用,参数名对应参数值 一目了然--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg name="name" value="小狂神3号"/> </bean> </beans> 2.关于类的复杂成员变量xml注入方式 实体类举例 public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Address{" + "address='" + address + '\'' + '}'; } } //***************************** public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String, String> card; private Set<String> games; private String wife; private Properties info; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } public List<String> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public String getWife() { return wife; } public void setWife(String wife) { this.wife = wife; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}'; } }相应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"> <bean id="address" class="com.kuang.pojo.Address"> <property name="address" value="西安"/> </bean> <bean id="student" class="com.kuang.pojo.Student"> <!--常量注入--> <property name="name" value="秦疆"/> <!--引用注入--> <property name="address" ref="address"/> <!--数组注入--> <property name="books"> <array> <value>《三国演义》</value> <value>《水浒传》</value> <value>《红楼梦》</value> <value>《西游记》</value> </array> </property> <!--list注入--> <property name="hobbys"> <list> <value>听歌</value> <value>敲代码</value> <value>看电影</value> </list> </property> <!--map注入--> <property name="card"> <map> <entry key="身份证" value="111111222222223333"/> <entry key="校园卡" value="123846518318131518"/> </map> </property> <!--set注入--> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>BOB</value> </set> </property> <!--空值注入--> <property name="wife"> <null/> </property> <!--property--> <property name="info"> <props> <prop key="driver">com.mysql.jdbc.driver</prop> <prop key="url">jdbc:mysql://localhost:3306/school?useSSL=true&useUnicode=true&characterEncoding=UTF-8</prop> <prop key="username">root</prop> <prop key="password">123456</prop> </props> </property> </bean> </beans> 3.拓展c、p 标签注入举例,以及作用域scope参数了解 实体类举例 package com.kuang.pojo; public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public User() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } } 相应xml示例 ```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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接输入属性的值:property 需要无参构造器和set方法 --> <bean id="user" class="com.kuang.pojo.User" p:name="秦疆" p:age="18"/> <!--c命名空间注入,通过构造器注入:construct-args 需要有参构造器 --> <!--scope 作用域 默认 单例 prototype:原型,每次获取都会生一个新的实例 --> <bean id="user2" class="com.kuang.pojo.User" c:name="狂神" c:age="18" scope="prototype"/> </beans>这里需要关注下,beans标签的类容,其中第三四行分别给出了c、p标签的引用声明 scope这个属性默认为singleton(单例的) 。 可以手动指定为prototype(原型的),这样每次获取同一个bean实例时,将会是不同的对象。
上面的一顿配置文件,估计觉得脑袋疼了,于是就开始简化配置文件
注解注入开始了! 配置文件变成了 (这里引入其它配置文件,beans中的各个beanid的重名问题,spring会自动匹配到可以唯一的一个bean,无法唯一时,就会报错)
<?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.kuang"/><!--扫描指定包下的类--> <context:annotation-config/><!--开启注解注入--> <import resource="beans.xml"/><!--引入其它xml配置文件--> <import resource="beans1.xml"/> <import resource="beans2.xml"/> </beans>然后我们需要注入的各个Java类,可以以注解的形式注入 (下面看看经典架构 手动滑稽)
package com.kuang.controller; import org.springframework.stereotype.Controller; @Controller public class UserController { } //******************************** package com.kuang.dao; import org.springframework.stereotype.Repository; @Repository public class UseerDao { } //******************************** package com.kuang.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class User { @Value("秦疆") public String name; } //********************************package com.kuang.service; import org.springframework.stereotype.Service; @Service public class UserService { }它们类名上方的注解都是在说,已被spring容器管理了。
脱离xml配置文件的注入。。。。 (已经没人能阻止spring瞎折腾了)
package com.kuang.config; import com.kuang.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration//标识为配置类,从xml演变过来,可以认为它就是 beans.xml文件的Java版 @ComponentScan("com.kuang.pojo")//对应上面那种的扫描配置 @Import(KuangConfig2.class)//对应上面那种的引入配置 public class KuangConfig { @Bean public User getUser() { return new User(); } } //****************************************** import com.kuang.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class KuangConfig2 { @Bean public User getUser() { return new User(); } }补充下常用的自动注入的注解
@Autowired 通过类型 匹配 有同类型不同名时,需要通过补充@qualifier(value=“xxx”)注解来指定名字以实现自动装配@Resource 通过类型或名字 匹配 二者都匹配不上时,可以在后面增加参数(name=“xxx”)我们需要知道,代理是在干啥。 代理就是把你要做的事情做一遍,那特么要代理有个锤子用? 在java层面,就是 原业务类A与该业务类的代理类a会实现按同一个接口,从而会做同一件事情。a存在的意义:实际生产环境中A所要实现的核心业务,往往会伴随很多其它的关联动作,有了a的存在,A就可以真正的专注于核心业务实现,而其它伴随的事情都交由a去做。即业务分离。
执行结果图 代理帮忙打了个日志,哈哈 可以看到,静态代理实现这个有个很大的弊端,会成倍的增加代码量,每一个业务接口都需要一个代理类! 于是,就会需要动态代理。 动态代理,就是为了,不要去写固定的代理类,利用,jdk提供的Proxy类和反射机制动态创建代理类。来实现
动态代理实现方式 基于接口实现:原生jdk 基于类实现:cglib 基于Java字节码实现:javassist
执行结果图同上! 动态代理实现过程主要关注两个比较重要的东西 InvocationHandler: 创建代理的处理者接口,实现它。 Proxy.newProxyInstance(arg1,arg2,arg3):Proxy类提供的一个静态方法,创建代理对象,它需要的三个参数类型分别是 arg1:类加载器(ClassLoader) arg2:需要被代理的业务接口(Interface) arg3:InvocationHandler 的对象(上面自身实现了这个接口,所以直接传this)
这样一来,就避开了 静态代理那个弊端了。 可以一个hander在调用时动态去创建同一业务接口的多个实现类的代理了
又:面向切面编程
有了上面代理模式的铺垫,aop就显得很自然了,上面是代码硬怼,而在spring中,那就是对代码过程中用的对象都抽离出来交给spring容器管理 还是示例吧
上述操作完毕,方法前后就会执行我们切入的操作了!
这种方式自定义切面,然后指定方法织入,感觉发挥空间挺大的
这种方式,就有点儿要脱离xml的意思了,直接注解类为切面。 然后在类的方法名上注解切入位置参数就是切点 常用的三种 方法前 @Before 方法后@After 环绕@Around(较为特殊需要一个ProceedingJoinPoint 类型的入参) 感觉就是代理的操作, 伴随核心业务,去增加一些附属的动作, 达到业务分离的目的!
事务特性:acid 原子性,一致性,隔离性,持久性 Spring支持,声明式事务 和 编程式事务(这种就是老套路,代码里面捕获异常然后调用rollback()方法) 而Spring要给出的,就是配置就完事儿了,我们只需要关注哪些地方需要事务就够了 对应到它的AOP思想,就式找准切入点!! 代码示例
<!--配置声明式事务--> <bean id="transcation" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"/> </bean> <!--结合aop配置织入事务--> <tx:advice id="txAdvice" transaction-manager="transcation"> <!--事务传播。。。。--> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" propagation="REQUIRED"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--proxy-target-class="false" 默认false jdk实现 如果设置为true则是cglib实现--> <aop:config proxy-target-class="false"> <aop:pointcut id="pointcut" expression="execution(* com.kuang.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> </aop:config>其中datasource 需要和你织入事务操作的datasource保持一致 对了,那些 tx标签之类的需要增加xml头声明引入(以下第4第5行还有对应的倒数第一二和三四行可以看见 )
<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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config/>真的是非入侵式!! 这要在这里配置这些东西(tx:attributes这个标签中指定方法名,支持通配),代码就具备了事务控制能力了!!