原创

Spring

1、Spring 框架

MyBatis框架是一个持久化的框架、负责数据访问

Spring MVC是Spring框架中的一个模块,负责控制器的管理

Spring是一个轻量级的容器框架

轻量级:针对EJB(Enterprise Java Bean)。

容器框架:Spring就是一个大工厂。程序中的工厂是用来生产对象的。容器就是一个生产、管理、维护对象的工厂

Spring框架的核心技术: IoC AOP

IoC(Inversion of Control): 控制反转

AOP: 面向切面编程 (面向方面编程)

Spring是一个基于Ioc和AOP的轻量级饿的容器框架

2、Spring 体系结构

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一样

3、IoC/DI是什么

1.依赖注入 : DI (Dependency Injection)

在运行期: 由外部容器动态地将依赖对象注入到组件中

** 2.控制反转: IoC (Inversion of Control)**

所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护由外部容器来负责的。这样控制就由应用本身转移到了外部容器,控制权的转移就是所谓控制反转。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,所谓控制反转就是:获得依赖对象的方式反转了。

依赖: 在程序开发中,对象之间存在依赖关系,比如:BIZ(业务层)依赖于DAO(数据访问层),Controller(控制层)依赖于BIZ程序开发是分层进行的。高内聚,低耦合。

依赖对象的主动获取

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对象,它是以主动获取的方式获得依赖对象。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接口,而接口只是一个规范,它是以被动接收的方式,获得依赖对象。依赖对象的创建由外部容器指定,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); 这句话就叫依赖注入

4、使用Spring IoC工厂创建对象

4.1 创建工程并导入Spring框架

img

在src目录下有个applicationContext.xml文件,他是Spring框架的核心配置文件

4.2 编写GoodsDAO并在Spring容器中创建对象

在Spring容器中创建对象,在applicationContext.xml文件中配置标记

<bean name="goodsDAO" class="com.zpark.tea_mgr.dao.impl.GoodsDAOImpl"/>

4.3 使用Spring上下文对象获得GoodsDAO对象

4.3.1 Spring上下文对象就是环境对象。读取Spring配置文件,生产对象

​ ApplicationContext接口

​ ClassPathXmlApplicationContext:基于类路径的方式加载Spring配置文件

4.3.2 调用getBean方法获得对象
ApplicationContext context = 
				new ClassPathXmlApplicationContext("/applicationContext.xml");
		GoodsDAO goodsDAO = (GoodsDAO) context.getBean("goodsDAO");
		goodsDAO.save();

4.4 编写GoodsBIZ并在Spring中创建对象

<bean name="goodsBIZ" class="com.zpark.tea_mgr.biz.GoodsBIZ"></bean>

4.5 在Spring容器中配置依赖注入

<bean name="goodsBIZ" class="com.zpark.tea_mgr.biz.GoodsBIZ">
	<property name="goodsDAO"> 	<!-- GoodsBIZA有个属性叫goodsDAO -->
		<ref bean="goodsDAO"/>	<!—依赖。引用了上面那个bean(1) Dao注入BIZ-->
	</property>
</bean>

4.6 获得GoodsBIZ对象

@SuppressWarnings("resource")
	ApplicationContext context = 
			new ClassPathXmlApplicationContext("/applicationContext.xml");
		
	/*GoodsDAO goodsDAO = (GoodsDAO) context.getBean("goodsDAO");
	goodsDAO.save();*/
		
	GoodsBIZ goodsBIZ = (GoodsBIZ) context.getBean("goodsBIZ");
	goodsBIZ.save();

5、使用构造方法创建对象,并使用构造注入

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();
	}
}

GoodsController goodsController = 
(GoodsController) context.getBean("goodsController");
goodsController.save();

<bean name="goodsController"
       class="com.zpark.tea_mgr.controller.GoodsController">
	<constructor-arg name="goodsBIZ" ref="goodsBIZ"/>
</bean>

6、 使用静态工厂创建对象

public class DAOFactory {
	public static GoodsDAO createDAO(){
	return new GoodsDAOImpl();
}
<bean name="dao1" class="com.zpark.tea_mgr.factory.DAOFactory" 
factory-method="createDAO"/>

7、使用实例工厂创建对象

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就是多例的,因为他有状态(属性)

8、 Spring的注解

8.1 导入注解使用的命名空间

8.2 启动注解

<context:annotation-config/>

8.3 配置注解的扫描路径

<context:component-scan 
base-package="com.zpark.tea_mgr.dao, com.zpark.tea_mgr.biz, com.zpark.tea_mgr.controller"/>

8.4 使用在类上的注解

注解作用
@Controller控制器层
@Service业务层
@Repository数据访问层
@Component通用(通常在你不知道他是什么身份时用)
@Scope默认是单例,如果是单例不要设置,要设置成多例时写为: @Scope(value=”prototype”)
@Lazy默认是false, 如果不需要懒加载,不要设置,要设置成懒加载时写为: @Lazy(value=true)

8.5 依赖关系上的注解

注解作用
@Autowired自动装配,by Type
@Qualifier(“value”)by Name ,通常作为@Autowired的补充说明,配套用
@Resource JSR-250规范效果等价于上两者同时使用
@PostConstruct修饰的方法会在对象被初始化时调用
@PreDestroy修饰的方法会在对象被销毁时调用

8.6 Spring的基于Java配置类的使用

@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();

9、 AOP (Aspect Oriented Programming)

Spring框架的两个核心概念: IoC 和 AOP

AOP: 面向方面(切面)编程 ,是处理实际开发中复杂业务的开发难度的

复杂的业务: 交叉的业务

一旦交叉出现,提高程序开发难度

最理想的开发状态是怎样的?分工,开发业务的人就只开发业务代码,开发事务的人仅开发事务就好

现在这个状态是开发时最理想的状态,开发业务的人只开发业务代码,开发通用功能的人只编写通用代码,这样,就达成了0耦合。但是, 开发时不存在0耦合的情况,现在,使用Spring容器工厂创建对象,创建的对象可以带有业务和通用功能。

将事务和日志这类的通用功能当成一个切面,切到我们所做的业务中,面向切面编程,就是在不影响你业务功能的基础上,附加其他的功能,在Spring工厂中实现AOP主要使用的是代理模式

10、 代理模式

10.1 代理:

有钱人买车: 1、交钱 2、提车

中间有很多复杂的事情,可以交给代理人,由代理人帮你完成你不想做的事儿

程序猿A: 保存业务 应该做事务

程序猿A的代理: 帮A做事务,并代理保存业务

代理模式: 就是在不影响你业务功能的基础上,附加其他的功能

代理分为静态代理和动态代理

静态代理: 被代理类实际存在,代理类也实际存在

动态代理: 被代理对象和代理类都不清楚,使用反射动态生成

10.2 不使用代理

public class GoodsBIZ {
    //保存业务和事务功能代码都要自己写
	public void save(){
		System.out.println("事务开始");
		System.out.println("保存业务");
		System.out.println("事务结束");
	}

10.3 使用静态代理的基于父类的代理

10.3.1 编写被代理的类
public class GoodsBIZ {
	public void save(){
		System.out.println("保存业务");
	}
10.3.2 编写针对GoodsBIZ的代理类
public class ProxyGoodsBIZ extends GoodsBIZ {
	@Override
	public void save(){
		System.out.println("事务开始");
		super.save();
		System.out.println("事务结束");
	}
10.3.3 测试
public class ProxyTest {
	public static void main(String[] args) {
		GoodsBIZ proxyGoodsBIZ = new ProxyGoodsBIZ();
		proxyGoodsBIZ.save();
	}

10.4 使用静态代理的基于接口的代理

10.4.1 声明一个接口,通过这个接口说明被代理的方法是什么
public interface BIZ {
	public void save();
}
10.4.2 编写被代理类,实现接口
public class GoodsBIZ implements BIZ {
	@Override
	public void save() {
		System.out.println("保存业务");
	}
10.4.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("事务提交");
	}

在代理类中有一个被代理接口的引用。这个引用将指向的是被代理对象

10.4.4 测试
public class ProxyTest {

	public static void main(String[] args) {
		BIZ goodsBIZ = new GoodsBIZ();
		
		BIZ proxyBIZ = new ProxyBIZ(goodsBIZ);
		
		proxyBIZ.save();
	}

10.5 找到切入点

@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("最终一刀");
		}
	}

10.6 抽取方面代码,编写切面代码的接口

10.6.1 声明一个接口,说明被代理的方法是什么?
public interface BIZ {
	public void save();
	}
10.6.2 声明一个方面接口
public interface AOP {
	public void before();
	public void after();
	public void err();
	public void end();
}
10.6.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();
		}
10.6.4 编写被代理的实现类
public class GoodsBIZ implements BIZ {

	@Override
	public void save() {
		System.out.println("保存业务");
		System.out.println(5/0);
	}
10.6.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("事务结束");
	}

10.6.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();
		}

10.7 基于JDK的动态代理

动态代理: 可以反射不针对具体的方法进行代理的代理类

这个动态代理类是根据反射的代码动态生成的,这个类我们看不到

10.7.1 Proxy类
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

newProxyInstance方法返回的就是一个代理类的实例。 代理类是在这个方法执行过程中动态创建的,我们看不到

ClassLoader: 类加载器,代理类是动态创建的,执行前没有这个类,他使用被代理对象的类加载器 Class<?>[] interfaces: 被代理的接口数组,基于JDK的动态代理只能针对接口 InvocationHandler h: 回调函数 回调函数就是动态生成的代理类要执行的源代码,它invoke方法里面的内容由我们编写

10.7.2 InvocationHandler接口

这个接口代表了动态代理类的行为

在这个接口中只有一个方法: invoke

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

Object proxy: 动态代理的对象 Method method: 当前代理的方法对象 Object[] args: 当前代理的方法的参数

在开发时,这个接口的实现类要程序员自己编写,编写的内容就是动态代理的代理内容

10.8 编写基于JDK的动态代理的工厂类

10.8.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();
		}
	}

}
10.8.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));
	}
}
10.8.3 测试
BIZ goodsBIZ = new GoodsBIZ();
AOP tranAOP = new TranAOP();
BIZ proxyBIZ = (BIZ) ProxyFactory.createProxyObjcet(goodsBIZ, tranAOP);
proxyBIZ.save();

评论

15 游客
dsadsad
我爱祖国 游客
博主好棒,爱了爱了