###hibernate一点补充,上一篇博客写不进去了
写在前面,补充一点,为什么需要ORM。当我们的数据库是面向关系的数据库(mysql,oracle),而我们的程序又是面向对象的,所以写代码的时候经常需要把类和表做转换。有了ORM就不需要了。。
####联合主键
关于马士兵hibernate比较细的知识点可以参考如下博客:自己写的太累了,把大量时间都浪费在记笔记和排版上面没有意义,别人有现成的为什么不拿来用呢? 站在巨人的肩膀上看世界。
http://blog.csdn.net/u012532092/article/details/79320934#t5
联合主键:对于数据库来讲,没什么变化。
但是对于编程来说,就得写一个主键类,然后在实体类中去引用它。
public class StudentPK implements Serializable {
private String id;
private String name;
}
为什么主键类要实现序列化? 因为有的时候内存满了,就要用虚拟内存(就是把硬盘上的一部分空间拿来当做内存来使用), 这个时候就需要把对象从内存中移到硬盘里去了,也就需要序列化了。
而且主键类还一定要重写equals和hashcode方法,要保证能区分开不同的对象。为啥要重写hashcode,因为对象信息也有可能被装进哈希表里面去。
####顺带说说视频中提到的hash表,也比较重要
哈希表在底层很多时候是数组来实现的,两个对象的哈希码相同的话,他俩会被放在数组里面同一个位置。所以里面数组的位置上往往放的是一个链表,链表上所有的对象都是哈希码相同的对象。
查找对象:首先计算对象的哈希码,通过哈希码找到在数组里的位置,这个链表里有多个对象,然后再遍历每个对象和自己比较看是不是equals即可。
hibernate面试中可能会问到的:
1、创建session的两种方式的区别:
openSession(永远是创建一新的session) 、getCurrentsession(当前环境如果有session了就会拿当前环境的)。
即:(和openSession区别,一个是每次产生一新的,一个是从上下文找。上下文主要有两种,一个是JTA,一个是thread)
检验办法如下:
什么叫当前环境已经有了session??session.commit()了 ,一提交或者一回滚session就关闭了,如果再需要就会产生一新的session。原来的session对象就消失了。如果没有commit(),那永远用的就是同一个。
2、getCurrentsession的用途:
1>、(界定事务边界,如addUser和加日志必须要放在一个事务里).。其余区别见笔记。
2. 事务提交自动close
3. 上下文配置可设置在xml文件中,而且必须要设定上下文,否则找不到session。
关于上下文:thread和jta的区别:
<property name="current_session_context_classs">thread/jta</property>
thread的意思是线程,如果要拿到session的话,就去当前线程里去找,看有没有session。有就直接拿,没有就创一新的。 就thread就行,其余的不用管。
4. 这里connection怎么去管理事务?之前JDBC讲过,就是conn.setAutoCommit(false);设为false,然后执行操作,一遇到异常就rollback,没异常就commit。。这种方式是依赖数据库的事务能力。
JTA:
但是有一种事务是一个connection管理不了的,那就是分布式事务。-- 也称为JTA事务。,
它由第三方的(中间件)事务管理器来commit,如果失败,就要求其中一个数据库rollback回滚。tomcat本身是不具备JTA能力的,它需要中间件的支持。
所以事务大概可以分为2种:一种是依赖于数据库本身的 ,简单的可以说成是connection事务,还有一种是JTA事务,是分布式的。
####session的一些操作
详细的还是去看现成的笔记,这里只记录认为重要的。
get、load的区别:
Object load(Class var1, Serializable var2); 注意,这里integer类实现了序列化接口
Students s = (Students) session.load(Students.class,1);
1、load一个不存在的记录的时候,如果不发SQL语句,那就不报错。 而get立马报错
2、load生成的是对象的一个代理,等到真正用到对象的内容时才发出sql语句。而get直接从数据库加载,不会延迟
稍微延伸下:
动态代理在hibernate,spring,mybatis里都用到了。
javassist是hibernate动态生成代理的类库,它可以直接的生成二进制码,它不会弄一个源码出来再去编译一下生成这样的一个类,
它会直接生成二进制码,所以hibernate的这种动态代理效率还是比较高的。
自己模拟的时候采用的是编译的方式,但是实际当中用的时候不会用那种方式,都是用一些类库直接生成二进制码。
update:
####对象的三种状态
详情还是请问笔记
缓存:每次从硬盘里读太慢了,就放到内存里,这样读起来效率高。缓存就是内存中的一块区域。
transient:内存中一个对象,没ID,缓存中没有那个key和value
persistent:session是内存中一个对象,里面有一个Map,key是1,value就是是t对象。这里就是一个缓存。
detached:session,close之后,缓存没了,但是t对象还是有的,只是t现在已经脱离了session 的管理了。这就是detached
####阶段总结
1、sessionfactory。它里面管理着一系列的连接池,它用来产生session,然后从池里面拿出一个connection出来,挂到session上面。 一般地,全局只需要一个sessionfactory就可以了,这也是为啥它是单例模式的原因。
2、上下文有两种,thread和JTA。区别:如果用thread,管事务的时候用的就是connection的事务,数据库连接的事务。如果是JTA,管事务的时候用的是application server帮你提供的事务管理器,这种事务管理往往用在分布式事务上面。当然不是分布式你要用也没错,但是没必要。
####关系映射(重要)
笔记中红色部分是重点,面试和工作用的都很多
数据库表与表之间的关系:只有外键这种。
这里的对象关系不是继承,而是数量上的关系。
###Sping – 讲太罗嗦,只看IOC AOP和springBoot就可以了
####概述
spring是现在java在开源界最火的框架
框架 --房屋架子
类库 – 水泥,钢筋等材料
####IOC
####马士兵spring
#####依赖注入或者控制反转,转到容器那里去了。。
马士兵的spring可以看看他写的spring.doc文档。。
反射机制:可以通过反射的api接口去探索运行期间的一个class的内部结构,并且根据它的内部结构来决定方法怎样进行调用。
spring开始:
面向接口编程(或者叫面向抽象类编程,讲完这个进而引出IOC。面向接口编程的好处就是灵活。
service层处理一些业务逻辑:权限认证等,报文转换等。
dao层:只是和数据库打交道。
依赖注入怎么实现:
读出id,id里面只要有property,看property名字是什么,生成set方法,用反射调一下,然后把bean注进来就可以。自己模拟的spring容器代码如下:
BeanFactory.java
public interface BeanFactory {
public Object getBean(String id);
}
bean.xml
<beans>
<bean id="u" class="com.bjsxt.dao.impl.UserDAOImpl" />
<bean id="userService" class="com.bjsxt.service.UserService" >
<property name="userDAO" bean="u"/>
</bean>
</beans>
首先把所有的bean放到容器中去,再看bean里面如果有子元素property则全部拿出来。再拼成set方法名字和方法的参数,
注意:@Component和@Resource完全可以替代xml的这种配置。效果是一样的,下面会讲到。。。
ClassPathXmlApplicationContext.java
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
//类名这么记:放在classpath下xml的应用上下文。
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map<String , Object> beans = new HashMap<String, Object>();
//IOC Inverse of Control DI Dependency Injection
public ClassPathXmlApplicationContext() throws Exception {
SAXBuilder sb=new SAXBuilder();
Document doc=sb.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml")); //构造文档对象
Element root=doc.getRootElement(); //获取根元素HD
List list=root.getChildren("bean");//取名字为disk的所有元素
for(int i=0;i<list.size();i++){
Element element=(Element)list.get(i);
String id=element.getAttributeValue("id");
String clazz=element.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
System.out.println(id);
System.out.println(clazz);
beans.put(id, o);
for(Element propertyElement : (List<Element>)element.getChildren("property")) {
String name = propertyElement.getAttributeValue("name"); //userDAO
String bean = propertyElement.getAttributeValue("bean"); //u
Object beanObject = beans.get(bean);//UserDAOImpl instance
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
System.out.println("method name = " + methodName);
Method m = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
m.invoke(o, beanObject); // service的实例、 UserDAOImpl的实例
}
}
}
public Object getBean(String id) {
return beans.get(id);
}
}
最后的测试类:
import org.junit.Test;
import com.bjsxt.dao.UserDAO;
import com.bjsxt.model.User;
import com.bjsxt.spring.BeanFactory;
import com.bjsxt.spring.ClassPathXmlApplicationContext;
public class UserServiceTest {
@Test
public void testAdd() throws Exception {
BeanFactory applicationContext = new ClassPathXmlApplicationContext();
UserService service = (UserService)applicationContext.getBean("userService");
User u = new User();
u.setUsername("zhangsan");
u.setPassword("zhangsan");
service.add(u);
}
}
依赖注入DI:userService里面userDao的属性是依赖容器来帮我们注入进来的,而不是写死的。
控制反转IOC:原来userDao是有当前类自己控制的,现在由容器帮忙控制,由运行上下文spring帮控制。好处:降低了耦合性,再者用配置文件注入修改起来方便而且灵活,反转到容器那里去了。
3. IOC容器
a) 实例化具体bean
b) 动态装配
4. AOP支持
a) 安全检查
b) 管理transaction
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
建议使用ApplicationContext(接口,bean factory 的子接口)还能有 生命周期等功能。。---细节要记住
注入的方式:
1、set 2、构造方法(用的不多)。 一般是set注入
spring注入类型,
5. <bean 中的scope属性 bean的声明周期。。这个比较重要,其实也就一句话的事儿
a) Spring_0600_IOC_Bean_Scope
b) singleton 单例
c) proptotype 每次创建新的对象
<bean id="u" class="com.bjsxt.dao.impl.UserDAOImpl" scope="single"/>
scope="single" -- 单例 对象1==对象2 true
scope="prototype" -- 多例 对象1==对象2 false
7.自动装配
@Component:就是把这个类初始化了。
@Resource:就是要注入哪个,默认是byName的方式
要注解的话,要先配置一个<context:annotation-config/> 配置了这句话后spring会初始化四个bean
spring注解是注解在我们源码上的,必须要有个东西去识别它,什么东西呢?就是初始化的四个bean去帮我们识别。
10. @Autowired --- 这个写在set上,表示把userDAO注入进来。和xml效果一样
a) 默认按类型by type。。
b) 如果想用byName,使用@Qulifier
c) 写在private field(第三种注入形式)(不建议,破坏封装)
d) 如果写在set上,@qualifier需要写在参数上..指定注入那个名字的bean
11. @Resource(重要)(用到set方法上,后面加名字,指定用的是哪个)
a) 加入:j2ee/common-annotations.jar 它是属于j2ee的
b) 默认按名称,名称找不到,按类型
c) 可以指定特定名称
d) 推荐使用
e) 不足:如果没有源码,就无法运用annotation,只能使用xml
12. @Component @Service @Controller @Repository -- spring会识别这四个,都当成是一个组件
用@Component要加上<context:compontent-scan base-package="...包名">去扫描所有的组件,也就是@Component
@Component(用在类上) -- 把这个类放到容器里,key就是这个类的名字,value就是生成的对象。也可以指定名字
写了@Component就不用在xml中配置了。那userService也可以加一个@Component,那它也不用在xml中配了。
@Component(value = "userDAOImpl")对应的@Resource(name = "userDAOImpl")
a) 初始化的名字默认为类名首字母小写
b) 可以指定初始化bean的名字
13. @Scope
14. @PostConstruct = init-method; @PreDestroy = destroy-method;
#####AOP:
加日志的业务逻辑思路:
1、继承。 不太好,因为继承太不灵活了。我从你继承了,就不能从其他类继承了,再个如果父类改了,子类也要跟着改,耦合性太强了。不好。
2、聚合。这种方式可以。在设计模式里,很多时候都用聚合代替了继承。
小插曲:IPO的意思,把公家的变成自己的。
JDK要想实现一个动态代理的话,这个类必须得实现一个接口,否则JDK是产生不了动态代理的。但是hibernate能产生,就算没有实现接口,hibernate也能产生动态代理,它是通过修改二进制码实现的。
补充动态代理的一点儿东西:
使用的过程:(产生的过程比较复杂,这里先不说了)
动态代理的应用场景:做权限,加日志,做审查(效率的检查),事务(可以在方法之前开启事务,方法之后提交事务,这样的话就不用自己写了),还可以做统一异常处理。也是面向切面编程的应用场景,非常灵活。spring的实现就是使用了JDK的动态代理,前提是这个被代理类要实现一个接口,不过就算没实现接口,spring也可以产生动态代理,它用的是CGLIB帮你产生的。
动态代理的任意方法的执行前,都可以自动的加上自己的业务逻辑。
再说一次使用JDK动态代理的过程:1、首先做一个被代理对象target出来,然后把它交给handler,代理调用的时候就知道要调用哪个被代理对象的方法了,2、然后使用newProxyInstance()来产生我们需要的代理对象。这个方法有三个参数,第一个参数是classloader,必须和被代理对象的classloader一样才行、第二个参数是它所实现的target.getClass().getInterfaces()。被代理对象实现了一个接口,产生的代理对象使用组合的方式,那它也要实现同样的接口。第三个参数是把handler传进去。。
spring使用动态代理的方式:一是annotation的方法二是用xml的方式。
######annotation方式的AOP
需要在beans.xml文件中加入<aop:aspectj-autoproxy/>,表示spring会帮我们去产生代理。意思是只要在spring的容器启动过程之中,扫描到了有个类需要产生代理(也就是使用了@Aspectj注解),它就会帮你产生代理。
内部实现是aspectj来实现的,aspectj是一个专门用来实现代理的框架。spring使用了它。
//注意这些类是从哪个包下面的。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogInterceptor {
//-- 关于切入点的语法,记住这一个就行这个包内任何类的任何方法,返回值是任何类型
@Pointcut("execution(public * com.bjsxt.service..*.add(..))")
public void myMethod(){};//这个方法名就是 切入点集合的名字。。以起方法名的形式给切入点集合起了个名字
//-- 方法执行之前先执行这段方法。被代理的对象必须被spring管理起来
//
@Before("myMethod()")
public void before() {
System.out.println("method before");
}
@Around("myMethod()")
public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("method around start");
pjp.proceed();
System.out.println("method around end");
}
}
######XML方式的AOP
用别人的切面逻辑的时候,只能用XML
因为别人的切面逻辑并没有写出在哪个方法上加(也就是没有@Pointcut这种逻辑,它事先也不知道往哪个方法上加啊。),你也不好往源码上加,所以这个时候就只能用XML。
所以spring的话,IOC的时候用annotation方便。 AOP就用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: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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="com.bjsxt"/>
<bean id="logInterceptor" class="com.bjsxt.aop.LogInterceptor"></bean>
<aop:config>
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:before method="before" pointcut="execution(public * com.bjsxt.service..*.add(..))" />
</aop:aspect>
</aop:config>
</beans>
######spring datasource
spring的数据源怎么配,?有c3p0,dbcp等。下面讲的是dbcp(数据库连接池)
JDK的java.sql包下有个接口叫DataSource。它有个方法getConnecttion(),就是拿connection的。为啥设计一个接口,因为拿到connection的方式有很多,它只需要定义接口让别人实现即可。也就是sun定义了一接口,让别人去实现。
dbcp – database connection pool数据库连接池的意思
怎么配?
这里就涉及到IOC的应用了。可以把datasource注入到DAOImpl里面。写一个datasource的接口,然后通过getConnecttion()方法拿到connection。以前的做法是加载驱动,获得连接,现在是写在xml中,用IOC注入。
<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="com.bjsxt" />
<!-- 方法一:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/spring" />
<property name="username" value="root" />
<property name="password" value="bjsxt" />
</bean>
-->
<bean 方法二: 方法一和方法二用的都是dhcp的数据库连接池
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:jdbc.properties</value>
</property>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sessionFactory" 方法三:
<!--
//这里的sessionFactory是spring的,而不是hibernate的,因为hibernate的sessionFactory是一个接口,这里spring是对它的实现。。这里要把方法二的datasource注入给它。。。
//这里在DAOImpl里面就直接注入AnnotationSessionFactoryBean就可以了,因为datasource已经注入到AnnotationSessionFactoryBean了。。
如何注入:在DAOImpl的set方法上写@Resource,默认是byName。
-->
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.bjsxt.model.User</value> //这里表示哪些类是hibernate的实体类,在这里配
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
</beans>
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=bjsxt
<property name="packagesToScan">
<list>
<value>com.bjsxt.model</value> 上面的配置可以通过这个标签来优化。。
</list>
</property>
######整合hibernate
这部分在spring的官方文档里面也有些。
首先是sessionfactory怎么构建,(回忆一下,SessionFactory对象就是 读取对象/关系映射文件 xxx.hbm.xml ),SessionFactory只需要一个就可以了,而spring管理的对象默认情况下就是一个单例,所以SessionFactory很适合交给spring去管理。。
关于整个hibernate的xml如何配,见上文,方法三。
DAOImpl里面怎么写,见下:
@Component("u")
public class UserDAOImpl implements UserDAO {
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Resource
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public void save(User user) {
//Hibernate
//JDBC
//XML
//NetWork
System.out.println("session factory class:" + sessionFactory.getClass());
Session s = sessionFactory.openSession();
s.beginTransaction();
s.save(user);
s.getTransaction().commit();
System.out.println("user saved!");
//throw new RuntimeException("exeption!");
}
}
初步总结spring、hibernate的整合顺序:
2. Spring整合Hibernate
a) <bean .. AnnotationSessionFactoryBean>
i. <property dataSource
ii. <annotatedClasses ----- 拿到sessionfactory,里面包含了datasource,标明哪些类需要加注解
b) 引入hibernate 系列jar包
c) User上加Annotation ----- @Entity等
d) UserDAO或者UserServie 注入SessionFactory
e) jar包问题一个一个解决
######整合过程中的事务管理 2个方式(说明一下tx就是transaction的意思)
spring的声明式事务就是AOP的运用。。
重点是要去理解,然后配什么去查文档即可。。。具体思路可以去看马士兵的spring.doc笔记
spring对各种各样的ORM框架都支持事务管理,现在看看hibernate的。
关于事务, 一定要找到它的起点和终点(专业名词叫事务的边界),应该放在service层,service会调用多个dao。如果写在DAO层,那意味着service的两个操作分别在两个事务里面,那一个dao抛异常,另一个dao是不会回滚的,也就是service只完成了一般的操作。。所以事务的边界应加在业务处理层。
那事务怎么加呢??
在一个方法上加事务,只需要加一个注解即可。另外在spring配置文件里面要声明一个tx,见下:表示是注解驱动的事务管理。
<tx:annotation-driven transaction-manager="txManager"/>//这里表示是注解驱动的事务管理
<bean id="txManager"
//它要通过一个connection来管理事务啊,(如果是跨数据库,那就要用JTA来管理事务)。
class="org.springframework.orm.hibernate3.HibernateTransactionManager"> //这是一个切面类。
//怎么告诉它这个connection是谁呢? 就是把sessionFactory给注进去,而sessionFactory里又注入了source,所以就拿到了数据库连接
//另外HibernateTransactionManager在进行事务管理的时候需要hibernate的一些配置,这些配置就是从sessionFactory中来的。
<property name="sessionFactory" ref="sessionFactory" />
</bean>
以上配置完之后,事务的处理就交给txManager了,它也是一个bean啊。 关系:datasource注入到sessionfactory,sessionfactory注入到txManager。。
然后就可以在service方法上声明了,这里面其实就是AOP的实现。service方法开始前beginTransaction,方法结束就transaction commit,有异常,它自动的rollback。
所以整合之后hibernate的session要用getCurrentSession,因为service方法开始的时候事务管理器生成了session。如果不指定,默认的上下文是thread。
如果中间抛出一个runtimeexception,那事务会回滚。
事务传播特性(有很多种,默认是required):propagation_required。。required的意思:
例子程序中,userTest里面的testAdd()去调用service里面带有事务的add()。。。如果别人(testAdd)调用我这方法(加了事务的方法add),如果已经有了事务了,就用原来那个,把自己当作事务的一部分加进去就可以了,如果没有就创一新的。。
以上就是spring对于hibernate的事务管理。
XML的事务配置如下:
至于用XML还是annotation,看情况。但是大多数还是XML配置,因为它可以同时配置好多方法。
<aop:config>
<aop:pointcut id="bussinessService"
expression="execution(public * com.bjsxt.service..*.*(..))" /> 搞个切面,在service的时候加
<aop:advisor pointcut-ref="bussinessService"
advice-ref="txAdvice" /> 通过这个指明一个事务。
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="getUser" read-only="true" />只读表示这里不能做修改操作,提高性能的。
<tx:method name="add*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
spring针对hibernate的使用上,有一些简单的封装,主要是hibernateTemplate,hibernateCallback,作用是简化编程。。
这里面有个设计模式叫,模版方法。。
i. 设计模式:Template Method
ii. Callback:回调/钩子函数
iii. 第一种:(建议)
- 在spring中初始化HibernateTemplate,注入sessionFactory
- DAO里注入HibernateTemplate
- save写getHibernateTemplate.save();
这里的话,就不用sessionfactory了,因为sessionfactory已经交给hibernateTemplate了。
这个时候就不要s.save了。直接hibernateTemplate.save就行了。这里就不要去管session了。
##孔浩的spring
1、引包
2、spring读取所有的文件都是在一个工厂里面读,这个工厂通过xml来配置,
原先创建类new的方式,这样依赖于实现类。
##慕课网 探秘Spring AOP
###1、5中advice注解
advice 描述的是你想在方法的执行之前,执行之后,或者是前后 要加入你要织入的代码。
主要是描述织入的时机,因此可以分为这5个部分。。
这点东西,在马士兵的spring笔记里面都有。。。
###2、spring AOP实现原理解析
####1、
大致内容如下:
AOP的代理实现:JDK的和CGLIB的。。。
spring AOP 的织入时机是 运行时 织入的。
那么 运行时织入是怎么实现的???答案:代理对象来实现的。。
####2、代理源码
静态代理
缺点:如果目标类有100个方法,那代理类需要对这100个方法进行委托,但是这100个方法前后需要执行的逻辑又都是一样的,所以就重复了。 所以产生了动态代理技术。
动态代理分为两大类:基于接口代理和基于继承代理。。他俩的代理分别是JDK代理和Cglib代理。。
#####JDK代理,实现源码:
1、首先调用newProxyInstance
Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class[]{Subject.class}, //注意这个接口的获取
new JdkProxySubject(new RealSubject()));
2、newProxyInstance会去调用getProxyClass0
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
getProxyClass0方法是去“寻找或者是生成指定的代理类”,它其实是先从缓存里面取,如果缓存没有它就去生成一个(看源码的方法注释),
这个缓存是从ProxyClassFactory中找,如果没有就生成。
3、getProxyClass0是从ProxyClassFactory(内部类)中生成Proxy代码的。
ProxyClassFactory生成的主要代码在apply方法,方法里找到接口的class对象,
Class<?> interfaceClass = Class.forName(intf.getName(), false, loader);
然后往下一步一步的进行 字节码的生成,
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( //看到这里是调用了ProxyGenerator来生成代码的
proxyName, interfaces, accessFlags);
4、ProxyClassFactory又是调动了ProxyGenerator(类)来生成代码
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
5、newInstance()。 最后生成字节码之后用反射new一个实例。
#####Cglib
有两点需要注意:
1、Cglib是通过继承的方式来实现代理类的,
2、Cglib是通过Callback(intercetor)的方式来织入我们想要的代码
MethodInterceptor和JDK的InvocationHandler很类似,同样是方法去反射调用目标对象的方法 。
####3、spring如何实现代理
#####源码
spring是如何创建AOP代理类的:
所以:总结:
如何强制使用Cglib代理:
#####责任链模式
多个AOP作用于一个对象的时候,他们是如何叠加到一起的。。 这里用到了 责任链模式。。
代码见码云。。。。com.reflect.dynamicProxyFromMukeWang.chain
#####spring AOP应用源码解析
有点复杂,自行看视频去。。
流程