使用Spring、Spring MVC、MyBatis组成SSM结构
IoC/DI、注解、AOP、动态代理
MyBatis框架是一个持久化的框架、负责数据访问
Spring MVC是Spring框架中的一个模块,负责控制器的管理
Spring是一个轻量级的容器框架
轻量级:针对EJB。现在EJB版本进行了更新。3.x
容器框架:Spring就是一个大工厂。程序中的工厂是用来生产对象的。容器就是一个生产、管理、维护对象的工厂
Spring框架的核心技术: IoC AOP
IoC: 控制反转
AOP: 面向切面编程 (面向方面编程)
Spring是一个基于Ioc和AOP的轻量级饿的容器框架

Spring是由七个组件构成的。在使用Spring框架时,七个组件可以组合使用,也可以拆分使用
1、 Spring Core:核心模块。整个Spring的功能都是基于这个模块的,他就是一个大工厂
2、 Spring AOP: 是面向切面编程的模块,他是Spring核心模块之一
3、 Spring DAO: Spring的数据访问模块,整合JDBC。对数据库进行访问时支持。声明式事务处理的组件
4、 Spring ORM: 这个组件是用来整合使用像MyBatis这种的ORM框架的组件
5、 Spring Context:上下文组件。整合环境用。ApplicationContext接口就在这个模块中
6、 Spring Web: 用来整合WEB框架的,如Strust2
7、 Spring MVC:是Spring对MVC模型的实现。功能上与Struts2一样
依赖注入 : DI (Dependency Injection)
在运行期: 由外部容器动态地将依赖对象注入到组件中
控制反转: IoC (Inversion of Control)
所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护由外部容器来负责的
这样控制就由应用本身转移到了外部容器,控制权的转移就是所谓控制反转
依赖: 在程序开发中,对象之间存在依赖关系,比如:BIZ依赖于DAO,Controller依赖于BIZ
程序开发是分层进行的。高内聚,低耦合。
(1) 控制层:Controller
(2) 业务层:BIZ
(3) 数据访问层: DAO
第一版:依赖对象的主动获取
public interface GoodsDAO {
public void save();
public class GoodsDAOImpl implements GoodsDAO {
@Override
public void save() {
System.out.println("GoodsDAOImpl.save() is called....");
}
public class GoodsBIZ {
private GoodsDAO goodsDAO = new GoodsDAOImpl();
public void save(){
System.out.println("GoodsBIZ.save() is called...");
this.goodsDAO.save();
}
在GoodsBIZ类中,依赖于GoodsDAOImpl对象
在GoodsBIZ类中,是以主动获取的方式获得依赖对象。new GoodsDAOImpl();这样,GoodsBIZ使用哪个DAO是谁说了算
BIZ与DAO耦合高
第二版: 依赖对象的被动接收
public interface GoodsDAO {
public void save();
public class GoodsDAOImpl implements GoodsDAO {
@Override
public void save() {
System.out.println("GoodsDAOImpl.save() is called....");
}
public class GoodsBIZ {
private GoodsDAO goodsDAO;
public GoodsDAO getGoodsDAO() {
return goodsDAO;
}
public void setGoodsDAO(GoodsDAO goodsDAO) {
this.goodsDAO = goodsDAO;
}
public void save(){
System.out.println("GoodsBIZ.save() is called...");
goodsDAO.save();
}
在GoodsBIZ类中,依赖于GoodsDAO接口,而接口只是一个规范
在GoodsBIZ类中,是以被动接收的方式,获得依赖对象。依赖对象的创建由谁说了算?
由外部容器指定
BIZ与DAO耦合低
public class SpringText {
public static void main(String[] args) {
GoodsBIZ goodsBIZ = new GoodsBIZ();
GoodsDAO goodsDAO = new GoodsDAOImpl();
goodsBIZ.setGoodsDAO(goodsDAO);
goodsBIZ.save();
}
总结:
GoodsBIZ由主动获取变成了被动接收,控制权发生了变化,这个变化就叫控制反转
因为当前的控制权编程了被动接收,依赖对象由外部容器给GoodsBIZ注入。是通过
setGoodsDAO(…)进行注入的
goodsBIZ.setGoodsDAO(goodsDAO); 这句话就叫依赖注入
在src目录下有个applicationContext.xml文件,他是Spring框架的核心配置文件
在Spring容器中创建对象,在applicationContext.xml文件中配置标记
5.3.1 Spring上下文对象就是环境对象。读取Spring配置文件,生产对象
ApplicationContext接口
ClassPathXmlApplicationContext:基于类路径的方式加载Spring配置文件
5.3.2 调用getBean方法获得对象
ApplicationContext context =
new ClassPathXmlApplicationContext("/applicationContext.xml");
GoodsDAO goodsDAO = (GoodsDAO) context.getBean("goodsDAO");
goodsDAO.save();
<bean name="goodsBIZ" class="com.zpark.tea_mgr.biz.GoodsBIZ">
</bean>
<bean name="goodsBIZ" class="com.zpark.tea_mgr.biz.GoodsBIZ">
<property name="goodsDAO"> <!-- GoodsBIZA有个属性叫goodsDAO -->
<ref bean="goodsDAO"/> <!—依赖。引用了上面那个bean(1) -->
</property>
</bean>
@SuppressWarnings("resource")
ApplicationContext context =
new ClassPathXmlApplicationContext("/applicationContext.xml");
/*GoodsDAO goodsDAO = (GoodsDAO) context.getBean("goodsDAO");
goodsDAO.save();*/
GoodsBIZ goodsBIZ = (GoodsBIZ) context.getBean("goodsBIZ");
goodsBIZ.save();
public class GoodsController {
private GoodsBIZ goodsBIZ;
public GoodsController(GoodsBIZ goodsBIZ){
System.out.println("GoodsController的有参构造被调用");
this.goodsBIZ = goodsBIZ;
}
public void save(){
System.out.println("GoodsController.save() is called...");
goodsBIZ.save();
}
}
<bean name="goodsController"
class="com.zpark.tea_mgr.controller.GoodsController">
<constructor-arg name="goodsBIZ" ref="goodsBIZ"/>
</bean>
GoodsController goodsController = (GoodsController) context.getBean("goodsController");
goodsController.save();
public class DAOFactory {
public static GoodsDAO createDAO(){
return new GoodsDAOImpl();
}
<bean name="dao1" class="com.zpark.tea_mgr.factory.DAOFactory"
factory-method="createDAO"/>
public GoodsDAO getDAO(){
return new GoodsDAOImpl();
}
<bean name="daoFactory" class="com.zpark.tea_mgr.factory.DAOFactory"/>
<bean name="dao2" factory-bean="daoFactory" factory-method="getDAO"/>
<bean name="dao1" class="com.zpark.tea_mgr.factory.DAOFactory"
factory-method="createDAO" scope="prototype"/>
scope属性默认为”singleton”,表示单例;取prototype代表多例
有些对象需要是多例的,比如Struts2中的Action就是多例的,因为他有状态(属性)
<context:annotation-config/>
<context:component-scan
base-package="com.zpark.tea_mgr.dao, com.zpark.tea_mgr.biz, com.zpark.tea_mgr.controller"/>
@Controller 控制器层
@Service 业务层
@Repository 数据访问层
@Component 通用(通常在你不知道他是什么身份时用)
@Scope 默认是单例,如果是单例不要设置
要设置成多例时写为: @Scope(value=”prototype”)
@Lazy 默认是false, 如果不需要懒加载,不要设置
要设置成懒加载时写为: @Lazy(value=true)
@Autowired by Type
@Qualifier(“value”) by Name ,通常作为@Autowired的补充说明,配套用
@Resource JSR-250规范,效果等价于上两者同时使用
@PostConstruct 修饰的方法会在对象被初始化时调用
@PreDestroy 修饰的方法会在对象被销毁时调用
@Configuration
@ComponentScan(value = {"com.zpark.tea_mgr.dao", "com.zpark.tea_mgr.biz"})
public class MyConfig {
}
等价于使用applicationContext.xml文件中的
<context:annotation-config/>
<context:component-scan
base-package="com.zpark.tea_mgr.dao, com.zpark.tea_mgr.biz />
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
GoodsBIZ goodsBIZ = context.getBean(GoodsBIZ.class);
goodsBIZ.save();
Spring框架的两个核心概念: IoC 和 AOP
AOP: 面向方面(切面)编程
AOP是处理实际开发中复杂业务的开发难度的
复杂的业务: 交叉的业务
一旦交叉出现,提高程序开发难度
最理想的开发状态是怎样的?分工。开发业务的人就只开发业务代码,开发事务的人仅开发事务就好
现在这个状态是开发时最理想的状态
开发业务的人只开发业务代码
开发通用功能的人只编写通用代码
这样,就达成了0耦合
但是, 开发时不存在0耦合的情况
现在,使用Spring容器工厂创建对象,创建的对象可以带有业务和通用功能
将事务和日志这类的通用功能当成一个切面
切到我们所做的业务中
面向切面编程,就是在不影响你业务功能的基础上,附加其他的功能
在Spring工厂中实现AOP主要使用的是代理模式
有钱人买车: 1、交钱 2、提车
中间有很多复杂的事情,可以交给代理人,由代理人帮你完成你不想做的事儿
程序猿A: 保存业务 应该做事务
程序猿A的代理: 帮A做事务,并代理保存业务
代理模式: 就是在不影响你业务功能的基础上,附加其他的功能
代理分为静态代理和动态代理
静态代理: 被代理类实际存在,代理类也实际存在
动态代理: 被代理对象和代理类都不清楚,使用反射动态生成
public class GoodsBIZ {
public void save(){
System.out.println("事务开始");
System.out.println("保存业务");
System.out.println("事务结束");
}
保存业务和事务功能代码都要自己写
1 编写被代理的类
public class GoodsBIZ {
public void save(){
System.out.println("保存业务");
}
2 编写针对GoodsBIZ的代理类
public class ProxyGoodsBIZ extends GoodsBIZ {
@Override
public void save(){
System.out.println("事务开始");
super.save();
System.out.println("事务结束");
}
3 测试
public class ProxyTest {
public static void main(String[] args) {
GoodsBIZ proxyGoodsBIZ = new ProxyGoodsBIZ();
proxyGoodsBIZ.save();
}
1 声明一个接口,通过这个接口说明被代理的方法是什么
public interface BIZ {
public void save();
2 编写被代理类,实现接口
public class GoodsBIZ implements BIZ {
@Override
public void save() {
System.out.println("保存业务");
}
3 编写代理类,实现同一个接口
public class ProxyBIZ implements BIZ {
private BIZ goodsBIZ;
public ProxyBIZ(BIZ goodsBIZ){
this.goodsBIZ = goodsBIZ;
}
@Override
public void save() {
System.out.println("事务开始");
goodsBIZ.save();
System.out.println("事务提交");
}
在代理类中有一个被代理接口的引用。这个引用将指向的是被代理对象
4 测试
public class ProxyTest {
public static void main(String[] args) {
BIZ goodsBIZ = new GoodsBIZ();
BIZ proxyBIZ = new ProxyBIZ(goodsBIZ);
proxyBIZ.save();
}
在上一版的代理类中观察,可以在被代理对象的方法调用的哪些位置附加代码?
@Override
public void save() {
try{
System.out.println("开始一刀");
goodsBIZ.save();
System.out.println("完成一刀");
}catch(Exception e){
e.printStackTrace();
System.out.println("异常一刀");
throw new RuntimeException(e);
}finally{
System.out.println("最终一刀");
}
}
12.6 抽取方面代码,编写切面代码的接口
1 声明一个接口,说明被代理的方法是什么?
public interface BIZ {
public void save();
2 声明一个方面接口
public interface AOP {
public void before();
public void after();
public void err();
public void end();
3 编写代理类
public class ProxyBIZ implements BIZ{
private BIZ biz;
private AOP aop;
public ProxyBIZ(BIZ biz, AOP aop){
this.biz = biz;
this.aop = aop;
}
@Override
public void save(){
try{
aop.before();
biz.save();
aop.after();
}catch(Exception e){
e.printStackTrace();
aop.err();
throw new RuntimeException(e);
}finally{
aop.end();
}
}
4 编写被代理的实现类
public class GoodsBIZ implements BIZ {
@Override
public void save() {
System.out.println("保存业务");
System.out.println(5/0);
}
5 编写方面代码: 事务切面代码
public class TranAOP implements AOP {
@Override
public void before() {
System.out.println("事务开始");
}
@Override
public void after() {
System.out.println("事务提交");
}
@Override
public void err() {
System.out.println("事务回滚");
}
@Override
public void end() {
System.out.println("事务结束");
}
6 编写测试类
public class ProxyTest {
public static void main(String[] args) {
BIZ goodsBIZ = new GoodsBIZ();
AOP tranAOP = new TranAOP();
ProxyBIZ proxyBIZ = new ProxyBIZ(goodsBIZ, tranAOP);
proxyBIZ.save();
}
动态代理: 可以反射不针对具体的方法进行代理的代理类
这个动态代理类是根据反射的代码动态生成的,这个类我们看不到
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces, InvocationHandler h) newProxyInstance方法返回的就是一个代理类的实例。 代理类是在这个方法执行过程中动态创建的,我们看不到 ClassLoader: 类加载器,代理类是动态创建的,执行前没有这个类,他使用被代理对象的类加载器 Class>[] interfaces: 被代理的接口数组
基于JDK的动态代理只能针对接口
InvocationHandler h: 回调函数
回调函数就是动态生成的代理类要执行的源代码,它invoke方法里面的内容由我们编写
这个接口代表了动态代理类的行为
在这个接口中只有一个方法: invoke
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
Object proxy: 动态代理的对象
Method method: 当前代理的方法对象
Object[] args: 当前代理的方法的参数
在开发时,这个接口的实现类要程序员自己辨析,编写的内容就是动态代理的代理内容
1、 ProxyHandler类
public class ProxyHandler implements InvocationHandler {
private Object obj;
private AOP aop;
public ProxyHandler(Object obj, AOP aop){
this.obj = obj;
this.aop = aop;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try{
aop.before();
Object proxyObj = method.invoke(obj, args);
aop.after();
return proxyObj;
}catch(Exception e){
e.printStackTrace();
aop.err();
throw new RuntimeException(e);
}finally{
aop.end();
}
}
}
2、 工厂类 ProxyFactory
public class ProxyFactory {
public static Object createProxyObjcet(Object obj, AOP aop){
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new ProxyHandler(obj, aop));
}
}
3 测试
BIZ goodsBIZ = new GoodsBIZ();
AOP tranAOP = new TranAOP();
BIZ proxyBIZ = (BIZ) ProxyFactory.createProxyObjcet(goodsBIZ, tranAOP);
proxyBIZ.save();
12.9 基于CGLIB的动态代理
CGLIB是一个第三方实现的动态代理的组件
12.9.1 导入CGLIB包
12.9.2 Enhancer类
通过Enhancer类的静态方法create方法来创建动态代理对象
Class type:类对象,被代理类的类对象
Callback callback:回调函数
12.9.3 Callback接口
这个接口代表了动态代理类的行为
一般开发时不直接使用这个Callback接口
而是使用MethodInterceptor接口
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
Object arg0: 当前被代理对象
Method arg1: 当前被代理对象的被代理方法
Object[] arg2:当前被代理方法对象的参数
MethodProxy arg3:当前被代理方法对象的代理方法对象
在这个接口中调用被代理对象的原方法
Object proxyObj = arg3.invokeSuper(arg0, arg2); 这个方法用来返回代理对象
public class ProxyInterceptor implements MethodInterceptor {
private AOP aop;
public ProxyInterceptor(AOP aop){
this.aop = aop;
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
try{
aop.before();
Object proxyObj = arg3.invokeSuper(arg0, arg2);
aop.after();
return proxyObj;
}catch(Exception e){
e.printStackTrace();
aop.err();
throw new RuntimeException(e);
}finally{
aop.end();
}
}
}
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static <T>T createProxyObj(Class<T> c, AOP aop){
return (T)Enhancer.create(c, new ProxyInterceptor(aop));
}
}
GoodsBIZ proxyGoodsBIZ = ProxyFactory.createProxyObj(goodsBIZ.getClass(), tranAOP);
proxyGoodsBIZ.delete();