-------------------------spring 学习 start--------------------
第一讲:
1、spring核心:IOC-控制反转(依赖注入) AOP-面向切面
2、spring配置文件如下(aaa.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
//里面写bean,在bean中配置类信息
<bean id="baseAction" class = "com.pb.sas.action.BaseAction" scope="prototype">
<property name="commonService"><ref local="batchCommonService"/></property>
</bean>
<bean id="id" parent="父类" class="包名+类名" scope="prototype(作用范围)">
<property name="loginService"><ref local="loginService"/></property>
<property name="属性"><ref local="关联的beanId"/></property>
</bean>
</beans>
解析:
xmlns:一个默认的命名空间
xmlns:xsi :也是命名空间 xsi=http://www.w3.org/2001/XMLSchema-instance
<property name="属性"><ref local="关联的beanId"/></property>:自动将关联的beanId注入到该类的那个属性当中,
这也是 IOC(控制反转) 的重要内容。
注意:property中name属性的值一定要 和 类中中属性名一致。。。。。。。。。
3、通过以下代码,加载spring配置文件。
ApplicationContext ac=new ClassPathXmlApplicationContext("aaa.xml");//加载spring配置文件
HelloWorld helloWorld=(HelloWorld)ac.getBean("id");//获取bean对象,通过beanId
helloWorld.say();//调用对象的方法
第二讲(IOC):
<property name="属性"><ref local="关联的beanId"/></property>:自动将关联的beanId注入到该类的那个属性当中,
这也是 IOC(控制反转) 的重要内容。
第三讲(依赖注入):
1、注入方式:
1.属性注入:<property name="id" value="1"></property>
2.构造方法注入(通过类型):<constructor-arg type="int" value="2"></constructor-arg>
构造方法注入(通过索引):<constructor-arg index="0" value="3"></constructor-arg>
构造方法注入(联合使用):<constructor-arg index="0" type="int" value="4"></constructor-arg>
3.工厂方法注入(非静态):<bean id="beanId" factory-bean="关联的beanId(工厂类的bean)" factory-method="工厂方法"></bean>
工厂方法注入(静态):<bean id="beanId" class="包名+类名(工厂类)" factory-method="工厂方法"></bean>)
注意:非静态与静态的区别: factory-bean 与 class
4.泛型依赖注入:
第四讲(bean注入):
1、关联bean注入:<property name="dog" ref="dog1(所关联的beanId)"></property>
2、内部bean使用(内部bean只能被当前类所使用):
<property name="dog">
<bean class="com.java1234.entity.Dog">
<property name="name" value="Tom"></property>
</bean>
</property>
3、null值(给dog属性赋空值):
<property name="dog">
<null></null>
</property>
4、级联属性(注意 要new dog这个对象,否则会报错):<property name="dog.name" value="Jack2"></property>
5、集合类型属性(注意都要new 集合对象):
5-1、list集合属性:
<property name="hobbies">
<list>
<value>唱歌</value>
<value>跳舞</value>
</list>
</property>
5-2、set集合属性:
<property name="loves">
<set>
<value>唱歌2</value>
<value>跳舞2</value>
</set>
</property>
5-3、map集合属性:
<property name="works">
<map>
<entry>
<key><value>上午</value></key>
<value>写代码</value>
</entry>
<entry>
<key><value>下午</value></key>
<value>测试代码</value>
</entry>
</map>
</property>
5-3、Properties集合属性:
<property name="addresses">
<props>
<prop key="address1">aaaaa</prop>
<prop key="address2">bbbbb</prop>
</props>
</property>
第五讲:
1、spring自动装配(慎用):spring会自动配置bean,根据default-autowire里面配置的信息
自动装配的前提:在配置文件的头部添加:default-autowire="",里面的值默认是default,还有其他值:
byName:通过名字进行自动配置,属性名和beanId自动匹配。
byType:通过类型进行自动配置,属性类型和class自动匹配(注意:只能匹配一个,多个匹配报错)
constructor(和byType类似):通过构造方法自动配置,也是根据类型(构造方法中的类型)去注入的。
<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"
default-autowire="default">
2、方法注入:
注意:spring 配置bean 默认是单例的,也就是多次获取的都是同一个对象。
scope="singleton" - 单例 这也是scope的默认值。
scope="prototype" - 多例
以下代码,每一次获取的都是不同的dog对象:
<bean id="dog" class="com.java1234.entity.Dog" scope="prototype">
<property name="name" value="Jack"></property>
</bean>
以下代码,通过People对象的获取的dog对象都是同一个对象,因为这个dog对象在一加载时候就已经配置好了。
<bean id="people1" class="com.java1234.entity.People">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="11"></property>
<property name="dog" ref="dog"></property>
</bean>
以下代码,通过People对象的获取的dog对象不是同一个对象,每一次获取到的都是新对象。
<bean id="people1" class="com.java1234.entity.People">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="11"></property>
<lookup-method name="getDog" bean="dog"/>
</bean>
注意:getDog是一个抽象方法,而People这个类是一个抽象,这个抽象方法是交给spring来实现的,这样才能保证每一次
获取的都是一个新对象。这也是方法注入的重要内容。
3、方法替换:
<bean id="people1" class="com.java1234.entity.People">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="11"></property>
<replaced-method name="getDog" replacer="people2"></replaced-method>
</bean>
<bean id="people2" class="com.java1234.entity.People2"></bean>
注意:People2 这个类要实现一个接口MethodReplacer,在接口的实现方法中定义替换getDog方法的方法体
结果:当执行People类中getDog这个方法时,实际上运行的people2中接口实现的方法。
第六讲:
1、bean之间的关系:
1.继承关系:
<bean id="abstractPeople" class="com.java1234.entity.People" abstract="true">//抽象类
<property name="className" value="高三5班"></property>//默认属性值
<property name="age" value="19"></property>
</bean>
<bean id="lisi" parent="abstractPeople">//parent 表示继承abstractPeople
<property name="id" value="2"></property>
<property name="name" value="李四"></property>
<property name="age" value="20"></property>//重定义属性值
</bean>
注意:People 这个类中的属性不仅包括自己的属性还有子类中定义的属性。
2.依赖关系:
<bean id="zhangsan" parent="abstractPeople" depends-on="autority">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
</bean>
<bean id="autority" class="com.java1234.service.Authority"></bean>
解析:spring配置文件的执行顺序是从上到下来执行的,但是如果有depends-on 这个属性的配置,表示执行当前
的这个bean执行需要依赖于depends-on这个属性配置的bean,这样会先去执行depends-on这个属性配置的bean,然后
才执行当前bean。
3.引用关系:<property name="dog" ref="dog"></property>
ref就是表示引用关系。
2、bean的作用范围(也就是scope的取值范围):
1.singleton:spring IOC容器中仅有一个bean实例,bean以单例的方式存在。(重点掌握)
2.prototype:每次从容器中调用bean时,都返回一个新的实例。(重点掌握)
3.request:每次HTTP请求都会创建一个新的bean。
4.session:同一个HTTP Session共享一个bean。
5.global session:同一个全局Session共享一个Bean,一般用于Portlet应用环境。
6.application:同一个Application共享一个Bean。
第七讲(AOP详解-概念):
1、AOP:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,
是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业
务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发
的效率。
主要功能:日志记录,性能统计,安全控制,事务处理,异常处理
意图:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为
的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
AOP理解:AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻
辑过程中各部分之间低耦合性的隔离效果。
第八讲(AOP实例讲解)实现流程如下:
1、使用AOp需要引入相关jar包:aopalliance.jar、aspectjweaver-1.6.6.jar、spring-aspects-4.0.6.RELEASE.jar
2、定义一个切面类及方法,如下:
public class StudentServiceAspect {
public void doBefore(JoinPoint jp){//注意:JoinPoint里面封装了好多信息,如:类名、方法名、参数等等
System.out.println("类名:"+jp.getTarget().getClass().getName());
System.out.println("方法名:"+jp.getSignature().getName());
System.out.println("开始添加学生:"+jp.getArgs()[0]);
}
public void doAfter(JoinPoint jp){
System.out.println("类名:"+jp.getTarget().getClass().getName());
System.out.println("方法名:"+jp.getSignature().getName());
System.out.println("学生添加完成:"+jp.getArgs()[0]);
}
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("添加学生前");
Object retVal=pjp.proceed();//这行代码表执行逻辑类的方法,如果执行的是void方法,则返回值为null
System.out.println(retVal);
System.out.println("添加学生后");
return retVal;
}
public void doAfterReturning(JoinPoint jp){
System.out.println("返回通知");
}
public void doAfterThrowing(JoinPoint jp,Throwable ex){
System.out.println("异常通知");
System.out.println("异常信息:"+ex.getMessage());
}
}
解析:JoinPoint就是切入点。
3、配置spring配置文件:
1.将配置文件头部定义为(注意文件头部的引入):
<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">
//以下是切面类
<bean id="studentServiceAspect" class="com.java1234.advice.StudentServiceAspect"></bean>
//以下是业务逻辑类
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean>
//以下是AOP配置信息
<aop:config>
<aop:aspect id="studentServiceAspect" ref="studentServiceAspect(切面类)">
//以下那行代码是切点 expression:表达式 execution:执行
//*(返回值) com.java1234.service(包名).*(类名).*(方法名)(..(参数)) 注意:*表示任意
//expression里面定义的内容是和业务逻辑类相关的,如:before表示在执行业务逻辑类之前执行
<aop:pointcut expression="execution(* com.java1234.service.*.*(..))" id="businessService"/>
//以下表示前置通知 before:在业务逻辑之前执行 method:切面类中的方法 pointcut-ref:切点
<aop:before method="doBefore" pointcut-ref="businessService"/>
//以下表示后置通知
<aop:after method="doAfter" pointcut-ref="businessService"/>
//以下表示环绕通知
<aop:around method="doAround" pointcut-ref="businessService"/>
//以下表示返回通知,也就是方法返回的时候给个通知,即使是void方法也会有通知
<aop:after-returning method="doAfterReturning" pointcut-ref="businessService"/>
//以下表示异常通知,有异常才会调用到该方法
<aop:after-throwing method="doAfterThrowing" pointcut-ref="businessService" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>
总结(实现流程):
1.导入所需要的相关jar包。
2.创建一个切面类及编写类中的方法,还有业务逻辑类(这就不用说了吧)。
3.在spring配置文件配置切面类和业务逻辑类的bean信息以及配置相关AOP配置信息。
3-1.AOP配置信息 包括:切面类、切点以及通知方法。
注意:AOP配置信息是怎么进行配置的
第九讲(spring对DAO的支持):
1、spring对JDBC的支持(主要是通过原生SQL对数据库进行操作):
1-1、引入相关jar:commons-dbcp-1.4.jar、commons-pool.jar、mysql-connector-java-3.1.12-bin.jar
spring-jdbc-4.0.6.RELEASE.jar、spring-tx-4.0.6.RELEASE.jar
1-2、配置spring配置文件(注意文件头部的引入):
<?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">
//以下bean是数据源配置,该数据源是Apache的数据源,需要导入相关jar,destroy-method表示连接用完后放回连接池
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>//驱动
<property name="url" value="${jdbc.url}"/>//url地址
<property name="username" value="${jdbc.username}"/>//用户名
<property name="password" value="${jdbc.password}"/>//密码
</bean>
//加载指定路径下的properties文件
<context:property-placeholder location="jdbc.properties"/>
//以下bean是spring关联数据库所用的,需导入相关jar包
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="studentDao" class="com.java1234.dao.impl.StudentDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
</beans>
1-3、properties文件配置如下:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_spring
jdbc.username=root
jdbc.password=123456
1-4、SQL语句的执行:都是通过jdbcTemplate执行SQL语句的
jdbcTemplate.update(sql,params);
总结(实现流程):
1.需要导入相关jar包。
2.在spring配置文件中配置数据源等配置信息(注意配置spring配置文件的头部信息)
3.编写相关的类(series、dao),并在spring中配置这些类,通过bean。
4.在dao层引入spring关联数据库的bean,通过这个bean来操纵数据库。
第十讲:
1、继承JdbcDaoSupport的好处:
1.JdbcDaoSupport中有jdbcTemplate这个属性了,不需要我们自己去定义这个jdbcTemplate属性了。
2.可以直接将数据源dataSource放在dao层了,然后数据源dataSource会被注入到JdbcDaoSupport中。
这样,我们直接使用JdbcDaoSupport中的jdbcTemplate属性就可以直接操作数据库了。
2、NamedParameterJdbcTemplate 和 jdbcTemplate的用法差不多相同,不过有以下的区别:
1.数据源的注入:
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
2、执行SQL语句的注入参数的不同:
String sql="insert into t_student values(null,:name,:age)";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("name", student.getName());
sps.addValue("age", student.getAge());
return namedParameterJdbcTemplate.update(sql,sps);
第十一讲:spring中事务的介绍:
1、事务的特性:原子性、一致性、隔离性、持久性
2、事务一般是发生在service层。
3、事务的原理是AOP,而AOP的原理的动态代理。
如果在调用过程中,只是类自身的调用,而不是代理对象去调用,那么就不会产生AOP,这样spring就不能把代码织入到约定的流程中,就不会产生事务,从而导致事务失效。
spring文档笔记:
1、ApplicationContext ac = new ClassPathXmlApplicationContext("aaa.xml",getClass());//创建IOC容器
2、spring简介:
1.spring是一个非常活跃的可以涉及全方位各层的JAVA开源框架
2.spring是一个基于IOC/DI和AOP思想来构架多层JavaEE系统的框架
IOC:控制反转,第三方对象,根据客户的需要,创建对象后,通过客户提供的方法
将对象传入到客户那,IOC强调的是由第三方创建对象,不是自已创建对象。
DI:依赖注入,本质上与IOC思想是相同的
DI强调的是第三方创建对象后,调用什么方法将对象传入,例如:set方法和constructor方法
3.为什么要使用Spring:
(1)简化企业开发,提升企业开发效率
(2)以一种非侵入式的方式来管理你的代码,spring提倡”无或最少侵入”原则
(3)有六大模块,你可以选用其中一个或多个模块,不必将整个spring六个模块导入
(4)提供了IOC容器和AOP的声明式事务管理,更方便高效开发
4.六大模块:
*Spring-core, 提供了IOC容器支持
*Spring-web, 对web开发支持(和struts整合、springmvc)
Spring-AOP, 面向切面编程(原理: 代理模式!)
Spring-orm 对象关系映射
Spring-jdbc jdbc操作支持
Spring-jee j2ee其他模块支持
3、spring配置详解:
1.指定bean是单例还是多例:
(1)scope="singleton" 单例,启动时候创建所有的代理的bean。当创建Dao/Service时候,可以指定singleton,
也可以不指定,默认就是单例!
(2)scope="prototype" 多例, 启动时候不会创建,【每次从容器获取实例时候,都会创建一个新的bean】
当创建Action时候必须使用!
2. 延迟初始化:
(1) lazy-init="false" 不延迟初始化
(2) lazy-init="true" 表示:在初始化容器时候,不创建单例的bean,对多例无效; 在第一次从容器中获取
时候创建实例;如果再次获取,获取是上次创建的单例的对象
3. 初始化与销毁方法:
(1)init-method="" 初始化方法, 创建完对象之后执行初始化方法; 如果是单例,执行一次; 多例就执行多次
(2)destroy-method="" 当调用ClassPathXmlApplicationContext对象的destroy方法的时候执行!
4.Bean生命周期:
(1)scope="singleton" - 单例
1. 如果bean作用域是单例, 看是否延迟初始化
1.1 如果没有配置延迟初始化(默认),或lazy-init=false创建容器对象的时候,
创建applicationContext.xml配置的所有单例对象。
1.2 如果配置lzy-init=true在创建容器对象后,在第一次从容器中获取实例时候,才创建对象
2. 创建完bean实例,执行init-metho配置的初始化方法;
3. 当销毁容器对象的时候,即调用ClassPathXmlApplicationContext对象的destroy方法的时候,
执行对象的destroy-method配置的对应方法;
(2)scope="prototype" - 多例 注意: 如果对象是多例,根lazy-init配置无效,影响不到!
1. 先创建IOC容器对象
2. 每次从容器获取bean,才创建对象
3. 创建完对象后,执行初始化方法 (每次创建对象后执行)
4. 对象创建完毕
5. 容器销毁,执行destroy方法
4、初始化IOC容器过程:(在web项目中可以将spring容器配置在web.xml文件中,让spring容器随web应用的启动而启动)
1)启动ioc容器,加载指定目录下的一个或多个xml文件
2)创建xml文件中配置的所有<bean/>标签指定的类对象,放入bean工厂这个map中保存
3) 如果<bean/>标签下有<property/>子标签的话,那么解析这个值,并封装到bean对象中
4) 如果<bean/>标签中有关系的话,还要设置bean之间的关系
默认情况下,创建bean工厂中的所有bean对象,执行无参构造器, bean工厂创建bean的顺序与
在spring.xml文件中配置的先后顺序相同
5、依赖注入,处理dao/service/action依赖关系:
<!-- 1. 创建dao/service/action对象; 2. 依赖注入 -->
<bean id="userdao" class="cn.itcast.e_eg.UserDao"></bean>
<bean id="userservice" class="cn.itcast.e_eg.UserService">
<property name="userDao" ref="userdao"></property>
</bean>
<bean id="userAction" class="cn.itcast.e_eg.UserAction" scope="prototype">
<property name="userService" ref="userservice"></property>
</bean>
6、几种代理模式:
(1) 静态代理:目标对象和代理对象 需要继承同一个类或实现相同的接口,在代理对象的方法中实现目标对象
的相同方法,还可以在实现目标对象方法的前后进行所需要的操作,如:事务的开启和提交。
特点: 1.静态代理在使用时,需要定义接口或是父类,被代理对象与代理对象一起实现相同的接口或者
是继承相同父类。
2.静态代理存在一个问题:当我们在被代理的类中增加了一个方法,代理类中也要增加相应方法。
(2)动态代理(JDK代理):参考网址:http://www.iteye.com/topic/517835
public class ProxyObject implements InvocationHandler{
private Object target; // 维护目标对象
public ProxyObject(Object target){
this.target = target;
}
public static Object factory(Object obj){
Class cls = obj.getClass();
//通过Proxy类的newProxyInstance方法来返回代理对象,返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
//cls.getClassLoader():返回该类的类加载器;cls.getInterfaces():确定此对象所表示的类或接口实现的接口;new ProxyObject(obj):指派方法调用的调用处理程序
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),new ProxyObject(obj));
}
/**
* 接口实现
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("函数调用前被拦截了: " + method);
if(args != null){
//打印参数列表
System.out.println("方法有 " + args.length + " 个参数");
for(int i = 0; i < args.length; i ++){
System.out.println(args[i]);
}
}
//利用反射机制动态调用原对象的方法
Object mo = method.invoke(target, args);
System.out.println("函数调用后进行处理 : " + method);
return mo;
}
//测试代码
public static void main(String agr[]){
interfaceTest si = (interfaceTest)factory(new test());
String f = si.eatFood(2);
}
}
注意:JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,但是代理类不需要
实现这些接口。如果想代理的类没有实现接口,就可以使用CGLIB实现。
动态代理的实现步骤:
1.创建一个动态代理类并实现InvocationHandler接口。
2.在代理类中定义存放目标类型的object对象以及一个有参的构造函数
3.定义一个静态方法用于返回一个指定接口的代理类实例。
4、对invoke拦截器进行实现
(3)CGLIB代理:
目标对象:
public class UserDao{//不能被final修饰
public void save() {
System.out.println("UserDao.save()"); }
public void delete() {
System.out.println("UserDao.delete()"); }}
子类代理:
public class ProxyFactory implements MethodInterceptor{
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象,创建代理对象(子类代理方式)
public Object getProxyInstance() {
// 创建一个生成器, 工具类
Enhancer en = new Enhancer();
en.setSuperclass(target.getClass());// 设置父类
en.setCallback(this); // 设置回调函数
return en.create();// 创建对象
}
// 事件处理程序
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("开始事务");
Object result = method.invoke(target, args); // 执行目标对象方法
System.out.println("提交事务");
return result;
}
}
测试: // 创建目标对象
UserDao target = new UserDao();
System.out.println(target.getClass());
// 创建代理对象
UserDao proxy = (UserDao) (new ProxyFactory(target).getProxyInstance());
System.out.println(proxy.getClass());
// 执行代理对象方法
proxy.save();
注意:Java原生代理会调用InvocationHandler中的invoke方法,而Cglib代理则会调用MethodInterceptor中的intercept方法
https://blog.csdn.net/tanga842428/article/details/52716875
区别:(1)JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,它有几个要求
* 实现InvocationHandler
* 使用Proxy.newProxyInstance产生代理对象
* 被代理的对象必须要实现接口
使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。
(2)CGLib 必须依赖于CGLib的类库,Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。
针对接口编程的环境下推荐使用JDK的代理。从执行效率上看,Cglib动态代理效率较高。在Hibernate中的拦截器其实现考虑到不需要其他接口的条
件Hibernate中的相关代理采用的是CGLib来执行。
7、Spring的AOP使用和原理:
1.AOP:面向切面编程
关注点代码: 重复执行的代码!
切面: 关注点代码形成的类,叫切面类!
切面类, 维护的就是重复执行的代码!
2.面向切面编程:
面向重复执行的代码编程!
重复的代码只要写一次, 在运行时期动态植入(到目标上)!
切面举例: 事务、权限、日志
1、spring源码研究之IOC容器在web容器中初始化过程:http://www.360doc.com/content/10/1223/08/1720440_80574231.shtml
1-1、web应用中创建spring容器有以下两种方式:
(1)直接在web.xml文件中配置创建spring容器。
(2)利用第三方MVC框架的扩展点,创建spring容器。
以上两种创建方式,第一种更加常见,为了让spring容器随web应用的启动而启动,
有如下两种方式:
(1)利用ServletContextListener实现。
(2)利用load-on-startup Servlet实现。
Spring提供ServletContextListener的一个实现类ContextLoaderListener,该类可以作为Listener 使用,
它会在创建时自动查找WEB-INF下的applicationContext.xml文件,因此,如果只有一个配置文件,
并且文件名为applicationContext.xml,则只需在web.xml文件中增加以下配置片段就可以了
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
如果有多个配置文件需要载入,则考虑使用<context-param...>元素来确定配置文件的文件名。
ContextLoaderListener加载时,会查找名为contentConfigLocation的初始化参数。因此,
配置<context-param...>时就指定参数名为contextConfigLocation。
带多个配置文件的web.xml文件如下:
<context-param>
<param-name>contextLoaderListener</param-name>
<param-value>
WEB-INF/*.xml, classpath:spring/*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
注意: 多个配置文件之间用“,”隔开。
2、spring中为啥action是多例模式而service、dao是单例模式:
我的理解:因为在action可能会有多个请求同时请求的情况出现,这时action需要对每个请求创建一个action对象用来保存每个请求所传递的参数,所以action
需要是多例模式。而service、dao只是接收action传递的参数,这些参数在一开始就已经定义好了。
3、spring事务理解:
1.事务最重要的两个特性,是事务的传播级别和数据隔离级别。
传播级别定义的是事务的控制范围,
事务隔离级别定义的是事务在数据库读写方面的控制范围。
-1:事务的7种传播级别:
1) PROPAGATION_REQUIRED ,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文
中不存在事务,则新建事务执行。
2)PROPAGATION_SUPPORTS ,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,
则使用非事务的方式执行。
3)PROPAGATION_MANDATORY , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码
遗漏添加事务控制的保证手段。
4)PROPAGATION_REQUIRES_NEW ,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事
务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
5)PROPAGATION_NOT_SUPPORTED ,这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,
执行当前逻辑,结束后恢复上下文的事务。
6)PROPAGATION_NEVER ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中
不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!
7)PROPAGATION_NESTED ,字面也可知道,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,
则新建事务。
嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,
这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。
-2:数据隔离级别分为不同的四种(由高到低):
(1)Serializable(串行化) :最严格的级别,事务串行执行,资源消耗最大;
(2)REPEATABLE READ(可重复读) :保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,
但是带来了更多的性能损失。
(3)READ_COMMITTED(读写提交) :大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。
该级别适用于大多数系统。
(4)Read Uncommitted(未提交读) :保证了读取过程中不会读取到非法数据。一个事务会读取到另一个事务未提交的数据
脏读 :所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,
这样事务A就形成了脏读。
不可重复读 :不可重复读字面含义已经很明了了,比如事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次
读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
幻读 :小的时候数手指,第一次数十10个,第二次数是11个,怎么回事?产生幻觉了?幻读也是这样子,事务A首先根据条件索引得到10条数据,
然后事务B改变了数据库一条数据,导致也符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产生了幻读。
注意:Oracle只支持读写提交和串行化,而MySQL却全部都支持。
tomcat数据源设置默认隔离级别:
-1:数据库默认隔离级别;1:未提交读;2:读写提交;4:可重复读;8:串行化
spring.datasource.tomcat.default-transaction=2
2.Spring配置声明式事务(步骤):
* 配置DataSource以及sessionFactory
* 配置事务管理器
* 事务的传播特性
* 那些类那些方法使用事务(主要是通过aop去使用)
注意:<1>Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,
一般变化的只是代理机制这部分。DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,
<2>除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法
3.根据代理机制的不同,Spring事务的配置又有几种不同的方式:
第一种方式:每个Bean都有一个代理
第二种方式:所有Bean共享一个代理基类
第三种方式:使用拦截器
第四种方式:使用tx标签配置的拦截器
第五种方式:全注解
spring面试题:https://blog.csdn.net/qq_33762302/article/details/70257803
https://www.cnblogs.com/yixianyixian/p/8372832.html
Spring事务配置的五种方式:https://blog.csdn.net/xuanjiewu/article/details/51604967
4、spring的一些知识点:
(1)给Spring 容器提供配置元数据的方式:
XML配置文件。
基于注解的配置。
@Component 把对象加入ioc容器,对象引用名称是类名,第一个字母小写
@Component(“name”) 把指定名称的对象,加入ioc容器
_____________________________________________________________
@Repository 同@Component , 主要用于标识加入容器的对象是一个持久层的组件(类)
@Service 同@Component , 主要用于标识加入容器的对象是一个业务逻辑层的组件
@Controller 同@Component , 主要用于标识加入容器的对象是一个控制层的组件
@Resource 注入属性(DI), 会从容器中找对象注入到@Resource修饰的对象上!
基于java的配置。
(2)Spring框架中的单例bean是线程安全的吗? 不,Spring框架中的单例bean不是线程安全的。
(3)Spring框架中bean的生命周期:
1.Spring容器 从XML 文件中读取bean的定义,并实例化bean。
2.Spring根据bean的定义填充所有的属性。
3.如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。
4.如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。
5.如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。
6.如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
7.如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。
8.如果bean实现了 DisposableBean,它将调用destroy()方法。
(4)有五种自动装配的方式,可以用来指导Spring容器用自动装配方式来进行依赖注入:
1 no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。
2 byName:通过参数名 自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有
相同名字的bean。
3 byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性
具有相同类型的bean。如果有多个bean符合条件,则抛出错误。
4 constructor:这个方式类似于byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
5 autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。
(5)自动装配的局限性是:
1 重写: 你仍需用 <constructor-arg>和 <property> 配置来定义依赖,意味着总要重写自动装配。
2 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
3 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
(6)Spring支持的事务管理类型:
1.编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
2.声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
(7)Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:
代理模式—在AOP和remoting中被用的比较多。
单例模式—在spring配置文件中定义的bean默认为单例模式。
工厂模式—BeanFactory用来创建对象的实例。
(8)Spring如何处理线程并发问题:Spring使用ThreadLocal解决线程安全问题
<1>ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
<2>在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什
么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
<3>而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。
因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,
可以把不安全的变量封装进ThreadLocal。由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。
但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用。
<4>概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。
前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
(9)Spring AOP里面的几个名词解释:
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。
连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点总是代表一个方法
的执行。
通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。
切入点(Pointcut):匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定
名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口
(以及一个对应的实现)到任何被代理的对象。
目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。
既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
AOP代理(AOP Proxy): AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,
AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving):把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时
(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
我的理解:spring aop是编写一段公用的代码,安装某种约定或规则,将符合约定或规则的代码织入到该段公用代码中运行。
执行流程:前置通知、环绕通知、通过反射执行代码逻辑、后置通知、返回通知(或 异常通知)
环绕通知(@Around)是一个取代原有目标对象方法的通知,当然它也提供了回调原有目标方法的能力。
AOP分两类,一类可以对方法的参数进行拦截,一类是对方法进行拦截,SpringAOP属于后者,所以Spring的AOP是属于方法级的
拦截示例:
任意公共方法的执行:execution(public * *(..))
任何一个以“set”开始的方法的执行:execution(* set*(..))
AccountService 接口的任意方法的执行:execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行: execution(* com.xyz.service.*.*(..))
定义在service包和所有子包里的任意类的任意方法的执行:execution(* com.xyz.service..*.*(..)) 第一个*表示匹配任意的方法返回值, ..(两个点)表示零个或多个,第一个..表示service包及其子包,第二个*表示所有类, 第三个*表示所有方法,第二个..表示方法的任意参数个数
5、ThreadLocal是如何解决线程并发问题的:https://my.oschina.net/clopopo/blog/149368
https://blog.csdn.net/lufeng20/article/details/24314381
1.官方对ThreadLocal的描述:
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部
变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)
相关联。
6、spring MVC的一些知识点:
(1)java EE应用的分层模型:
1.Domain Object(领域对象)层:此层由一系列的POJO(Plain Old Java Object,普通的、传统的java对象)组成,这些对象是该系统的Domain Object(领域对象),
往往包含了各自所需实现的业务逻辑方法 实体类
2.DAO(Data Access Object,数据访问对象)层:此层由一系列的DAO组件组成,这些DAO实现了对数据库的创建、查询、更新和删除(CURD)等原子操作。 dao
3.Service(业务逻辑)层:此层由一系列的业务逻辑对象组成,这些业务逻辑对象实现了系统所需要的业务逻辑方法。 service
4.Controller(控制器)层:此层由一系列控制器组成,这些控制器用于拦截用户请求,并调用业务逻辑组件的业务逻辑方法,处理用户请求,并根据处理结果向
不同的表现层组件转发。 Action
5.View(表现)层:此层由一系列的jsp页面、Velocity页面、PDF文档视图组成,负责收集用户请求,并显示处理结果。 jsp
(2)Java EE 应用的组件:表现层组件、控制器组件、业务逻辑组件、DAO组件、领域对象组件
(3)MVC三层架构:
M:模型(Model)层 - JavaBean
V:视图(View)层 - jsp
C:控制器(Controller)层 - servlet spring
7、springjdbc和原生的jdbc有什么区别:
Spring的jdbc:节省代码,不管连接(Connection),不管事务、不管异常、不管关闭(con.close() ps.close )
JdbcTemplate(dataSource):增、删、改、查
TransactionTemplate(transactionManager):进行事务处理
了解springjdbc的网址:https://blog.csdn.net/wanghuiqi2008/article/details/46239753
8、使用spring的原因(spring的优点):
1.使用Spring的IOC容器,将对象之间的依赖关系交给Spring,降低组件之间的耦合性,让我们更专注于应用逻辑。
2.可以提供众多服务,事务管理,WS等。
3.AOP的很好支持,方便面向切面编程。
4.对主流的框架提供了很好的集成支持,如hibernate,Struts2,JPA等。
5.Spring DI机制降低了业务对象替换的复杂性。
6.Spring属于低侵入,代码污染极低。
7.Spring的高度可开放性,并不强制依赖于Spring,开发者可以自由选择Spring部分或全部。
缺点:Spring不支持分布式,这也是EJB仍然在用的原因之一。
9、spring mvc的原理:SpringMVC框架是以请求为驱动,围绕Servlet设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是DispatcherServlet,它是一个Servlet,顶层是实现的Servlet接口。
运行流程:
(1)客户端(浏览器)发送请求,直接请求到DispatcherServlet。
(2)DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
(3)解析到对应的Handler后,开始由HandlerAdapter适配器处理。
(4)HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
(5)处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。
(6)ViewResolver会根据逻辑View查找实际的View。
(7)DispaterServlet把返回的Model传给View。
(8)通过View返回给请求者(浏览器)
9.1、handlerMappings作用:注册和查找执行程序,作用是根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到 HandlerExecutionChain 对象中。在 HandlerMapping 接口的内部只有一个方法:
HandlerExecutionChain getHandler(HttpServletRequest request);
HandlerMapping 是由 DispatcherServlet 调用,DispatcherServlet 会从容器中取出所有 HandlerMapping 实例并遍历,让 HandlerMapping 实例根据自己实现类的方式去尝试查找 Handler。
9.2、handlerMappings类型():它们分别用于处理不同类型的Controller
RequestMappingHandlerMapping:处理注解@Controller或@RestController注解方式来声明一个Controller,然后使用@RequestMapping来修饰类名或方法名
SimpleUrlHandlerMapping:处理采用实现Controller接口或实现HttpRequestHandler接口的类
10、spring boot在使用jpa操作数据库时,需要用一个接口去继承JpaRepository<T,ID extends Serialzable>,但是这个接口不需要去实现,因为这些spring会根据JPA接口帮我们完成。
注意:需要用@EnableJpaRepositories去扫描该接口,将其加入到spring ioc容器中
10.1、JpaRepository接口具有增删改查,分页,排序等功能
10.2、jpa按照一定规则命名的方法也可以在不写任何代码的情况下完成查询。
如:findByNameLike(String name):按照名称进行like查询
getUserById(int id):根据Id查询
findByNameLikeOrNoteLike(String name):按照名称进行like查询或备注模糊查询
注意:这里的命名是以动词(get/find)开头,by表示按照什么内容查询
11、日志配置
logging.level.root=WARN #root日志以WARN级别输出
logging.level.org.springframework.web=DEBUG #org.springframework.web包下的日志以DEBUG级别输出
logging.level.org.hibernate=ERROR #org.hibernate包下的日志以ERROR级别输出
12、声明式事务的使用就是使用@Transactional这个事务注解,可以配置以下属性
1.事务管理器
2.指定事务传播行为 如:propagation=Propagation.REQUIRED
3.指定事务隔离级别 如:isolation=Isolation.READ_COMMITTED 读写提交隔离级别
4.指定超时时间 如:timeout=1 表示超时时间为1秒
5.是否是只读事务
6.设置在发生指定异常时,是否进行事务的回滚操作(默认是所有异常都回滚)
13、TransactionDefinition:事务定义器,它是依赖于我们配置的@Transactional的配置项生成的,通过它就能够设置事务属性。
14、事务管理器:
1.依赖于mybatis-spring-boot-starter就会自动创建一个DataSourceTransactionManager对象,作为事务管理器
2.依赖于spring-boot-starter-data-jpa就会自动创建一个JpaTransactionManager对象作为事务管理器
15、事务的第一类丢失更新和第二类丢失更新
因为加入了事务隔离级别,而标准定义的所有隔离界别都不允许第一类丢失更新发生,所以第一类丢失更新的问题在现代数据库中已经不会出现了。
-------------------------spring 学习 end--------------------