总结昨天的内容:
设计模式
最佳实践(经验)
特点
s:单一职责
o:开闭原则
l:里氏替代原则
i:接口隔离
d:依赖倒置
具体的设计模式
3.1单例(重要)
应用程序对应的实例只有一个
1、构造方法私有
2、包含自己的成员变量
3、提供静态方法给其他人调用
线程不安全的懒加载(在方法上开始初始化的)
线程安全的懒加载(在方法上开始初始化的,并且在方法上加锁sync)
线程安全的立即加载(在静态成员变量上直接初始化)
线程安全的立即加载(在静态代码块中初始化)
线程安全的懒加载(静态内部类实现,当你调用到静态内部类的方法的时候,静态内部类才加载) → 将实例初始化的过程放在了静态内部类中
3.2工厂
3.2.1简单工厂
给工厂方法的形参中 给不同的值,生产出的实例有所不同
当需要新的生产实例的时候,需要去修改工厂的生产方法
3.2.2工厂方法
更好的满足了开闭原则
提供一个工厂接口,提供生产方法的规范
具体的工厂实现工厂接口,在具体的工厂中提供详细的生产方法
3.2.3使用方式
3.2.3.1实例工厂
工厂中的生产方法非静态方法
先对工厂进行实例化,然后通过工厂对象来调用生产方法
3.2.3.2静态工厂
工厂中的方法是静态方法,可以直接调用。静态工厂类似于工具类。
3.3代理(非常重要)
给委托类实现原有的功能的基础上,并且进行增强
3.3.1静态代理
3.3.1.1委托类成员变量
委托类以代理类中的成员变量的形式存在,可以调用委托类对象的方法
在HouseProxy中定义了一个HouseOwner的成员变量
3.3.1.2代理类继承委托类
重写父类(委托类)的方法,并且在重写的方法中调用父类的方法(super来调用)
3.3.2动态代理
都是要获得一个代理对象,通过代理对象调用的方法才是增强的方法
3.3.2.1jdk动态代理
类要有接口的实现,生成的代理对象和接口相关。并且要用接口来接收。
HelloService helloService = new HelloServiceImpl();
Object proxy = Proxy.newProxyInstance(classloader,interfaces,new InvocationHandler(){
public Object invoke(proxy,method,args){
//前面的增强
Object object = method.invoke(helloSerivce,args);
//后面的增强
return object;
}
});
Object proxy只能用接口来接收,不能用实现类。
3.3.2.2cglib动态代理
invocationHandler和jdk动态代理的invocationHandler是不同包下的,不是同一个
Object proxy = Enhancer.create(class,new InvocationHandler(){
public Object invoke(proxy,method,args){
//前面的增强
Object object = method.invoke(helloSerivce,args);
//后面的增强
return object;
}
});
Object proxy可以用接口也可以用实现类来接收。
因为cglib的实现是用代理类继承委托类。根据里氏替换原则,可以用父类接收子类。
3.4建造者builder
更侧重于设置参数的过程
在builder中 在创建builder时(生产之前)已经完成了实例化
需要保证始终对同一个实例设置参数,实例就要求是全局变量
set方法如果想要连续用起来,要求这些set方法的返回值为builder
.setHeadIq(120).setHeadEq(120).setLegLength()
Spring介绍(4天)
SpringIOC
SpringAOP
Spring事务
Spring 概念
Spring框架其实就是偏基础性的框架。可以去整合其他框架。类似于平台。
核心:IOC、DI和AOP
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。
它是为了解决企业应用开发(JavaEE)的复杂性而创建的。框架的主要优势之一就是其分层架构(MVC分层),分层架构允许使用者选择使用哪一个组件,同时为 J2EE(JavaEE) 应用程序开发提供集成的框架。
Spring是一个分层的Java SE/EE full-stack(一站式) 轻量级开源框架。
IOC&DI
IOC用于实例化类对象,管理类对象
IOC:Inverse of Controll 控制反转
控制:实例的生成权
反转:由应用程序反转给spring
容器:容器是放置实例对象的地方 → Spring容器、IOC容器
原先实例我们想用的时候自己new出来(主动的过程);到了Spring阶段,把实例的生成权交给了Spring容器,由Spring容器进行生成并管理,当我们想用的时候就从容器中取出来。
例子:小明挣了10块钱自己花。
小明找到了对象,挣的10块钱交给了对象。小明想花5毛,就要跟对象申请。
所谓IOC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。
所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
DI:Dependency Injection 依赖注入
应用程序(贫穷) ←→ Spring容器(富有)
经过了控制反转,谁富有谁贫穷
依赖:
谁依赖谁? 小张依赖他对象 → 应用程序依赖Spring容器
为什么依赖? 小张对象有钱 → Spring容器包含了应用程序必须的内容
注入:
谁注入谁? Spring容器注入给应用程序
注入了什么?应用程序运行所必须的资源
谁依赖了谁:应用程序依赖于IOC容器
为什么要依赖:应用程序需要IOC容器来提供对象需要的外部资源
谁注入谁:IOC容器注入应用程序某个对象,应用程序依赖的对象
注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据)
aop:面向切面编程→ aspect oriented programming
oop→ object oriented programming
Spring Introduction
声明式事务支持:指定方法增加事务 → 通过注解指定方法 → 指哪儿打哪儿
事务 → 保证使用的是同一个connection → aop阶段会布置对应的作业
Spring的单元测试 → 提供了Spring环境
Spring 特点:
Core technologies: dependency injection, events, resources, i18n, validation, data binding, type conversion, SpEL, AOP.
依赖注入、资源、i18n国际化(springmvc)、校验(Springmvc)、数据绑定、类型转换(springmvc)、SpringExpressionLanguage、Aop(面向切面编程)
Core Container是Spring的核心组件。Spring的核心依赖就在这。
Spring的核心依赖:5+1
spring-core、spring-context、spring-aop、spring-beans、spring-expression
commons-logging(jcl)
写一个入门案例:
package com.cskaoyan.service;
public class HelloService {
public void sayHello(String name) {
System.out.println("hello " + name);
}
}
想要通过Spring容器管理HelloService对象,就得先创建一个Spring容器。
通过Spring配置文件来管理组件
xml的文件 → 通常名字叫application(-xx).xml
既然是xml文件,文件要满足一定的约束(schema),约束就是规定你书写的格式是什么样的。
约束怎么来?
1、复制已有的配置文件的约束
2、从spring的参考文档上的appendix上复制
3、通过创建文件模板来使用 (推荐)效率高
第一次学习,我们只能通过第二种方式来使用。
有了第一次后,后面再创建Spring配置文件就用第三种方式:
那么,如何创建文件模板?
a. 创建
b. 使用模板
选择自己创建的模板
4. 将组件交给spring管理
组件:交给spring容器管理的实例,我们称之为组件
注册:在spring的配置文件中如何定义实例
HelloService交给Spring管理,这个过程叫做注册
将控制权反转也是通过反射技术来实现的。Spring通过class就能生产处这个类的实例对象。
package com.cskaoyan;
import com.cskaoyan.service.HelloService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 入门案例1
*/
public class IocTest {
@Test
public void mytest1(){
//初始化一个应用上下文,应用上下文是Spring容器一个高级功能的实现
//再看ClassPath,表示类加载路径
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//通过应用上下文从Spring容器中取出组件(实例)
HelloService helloSerivce = (HelloService) applicationContext.getBean("helloSerivce");
helloSerivce.sayHello("ligenli");
}
}
这段代码中用到了ClassPath,下面就来讲一下classpath(类加载路径)。
a. maven的classpath
maven是根据文件夹名和目录结构来决定classpath
--src
--main
--java
--resources
java和resources是maven项目的classpath
4.2idea中的classpath
应用要在idea中跑起来
locTest:
package com.cskaoyan;
import com.cskaoyan.service.HelloService;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 入门案例1
*/
public class IocTest {
@Test
public void mytest1() {
//初始化一个应用上下文,应用上下文是Spring容器一个高级功能的实现
//再看ClassPath,表示类加载路径
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//通过应用上下文从Spring容器中取出组件(实例)
HelloService helloService = (HelloService) applicationContext.getBean("helloService");
helloService.sayHello("ligenli");
// 1、指定id获得实例对象
// 2、指定类型(class)获得实例对象
// 3、1+2,即指定id和class
//条件:这个类型(class)的组件在容器中只有这一个
HelloService helloService2 = (HelloService) applicationContext.getBean(HelloService.class);
helloService2.sayHello("加油吧!");
HelloService helloService3 = applicationContext.getBean("helloService", HelloService.class);
helloService3.sayHello("just be you");
}
@Test
public void mytest2(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//从容器中,取出指定id的组件多次,看是否是同一个组件
Object helloService = applicationContext.getBean("helloService");
Object helloService2 = applicationContext.getBean("helloService");
Object helloService3 = applicationContext.getBean("helloService");
Object helloService4 = applicationContext.getBean("helloService");
Object helloService1 = applicationContext.getBean("helloService");
Assert.assertEquals(helloService,helloService2);
}
}
入门案例2
也是通过spring容器管理组件;
管理多个组件 → 多个组件之间有依赖关系
HelloService依赖于HelloDao → 在Spring容器中如何维护多个组件之间的关系
HelloDao :
package com.cskaoyan.dao;
public interface HelloDao {
public void daoSayHello(String name);
}
HelloDaoImpl :
package com.cskaoyan.dao;
public class HelloDaoImpl implements HelloDao{
@Override
public void daoSayHello(String name) {
System.out.println("hello " + name);
}
}
HelloService :
package com.cskaoyan.service;
public interface HelloService {
public void sayHello(String name);
}
HelloServiceImpl :
package com.cskaoyan.service;
import com.cskaoyan.dao.HelloDao;
import com.cskaoyan.dao.HelloDaoImpl;
public class HelloServiceImpl implements HelloService{
//我们自己不实例化对象,让Spring来帮我们实例化
//HelloDao helloDao = new HelloDaoImpl();
HelloDao helloDao;
@Override
public void sayHello(String name) {
helloDao.daoSayHello(name);
}
public HelloDao getHelloDao() {
return helloDao;
}
public void setHelloDao(HelloDao helloDao) {
this.helloDao = helloDao;
}
}
package com.cskaoyan;
import com.cskaoyan.dao.HelloDao;
import com.cskaoyan.service.HelloService;
import com.cskaoyan.service.HelloServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void mytest(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//HelloServiceImpl bean = applicationContext.getBean(HelloServiceImpl.class);
//spring建议通过接口的类型取出,更容易规范你能去做的事情
//比如给spring的组件增强,比如使用jdk动态代理的方式进行增强,那么增强后的组件和接口是相关联的
HelloService helloService = applicationContext.getBean(HelloService.class);
helloService.sayHello("songge");
}
@Test
public void mytest2(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
HelloService helloService = applicationContext.getBean(HelloService.class);
helloService.sayHello("songge");
HelloDao helloDao = applicationContext.getBean(HelloDao.class);
System.out.println(111);
}
}
总结:Spring在这里有没有带来新的功能?并没有
调用的都是原有的方法sayHello和daoSayHello
但是提高了组件的重用频率
类似于搭积木,你需要什么样子的,就从容器中取出来。
学习了IOC 和 DI 最大的感触是什么?
控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法
依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。
Spring 核心API:
ApplicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
都是去加载application
ClasspathXmlApplicationContext 加载classpath目录下的配置文件
FileSystemXmlApplicationContext 加载文件系统目录下的配置文件
package com.cskaoyan;
import com.cskaoyan.service.HelloService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MyTest {
@Test
public void mytest(){
//从classpath加载配置文件→ java或resources目录
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
HelloService bean = applicationContext.getBean(HelloService.class);
}
@Test
public void mytest2(){
//D:\WorkSpace\j22_workspace\codes\day02-spring-ioc\demo3-applicationContext\src\main\resources\application.xml
String fileSystemPath = "D:\\WorkSpace\\j22_workspace\\codes\\day02-spring-ioc\\" +
"demo3-applicationContext\\src\\main\\resources\\application.xml";
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(fileSystemPath);
}
}
BeanFactory
BeanFactory:容器中所有的组件都是通过这个Bean生产出来的
BeanFactory:生产所有组件bean
FactoryBean:XXXFactoryBean,factoryBean对应的特定的xxx实例。即生产特定的Bean。
讲一个辅助编译的工具:Lombok
帮我们生成getter、setting、equal、hashcode、toString、有参无参构造方法
使用起来非常简单
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.cskaoyan.component;
public class NoArgsConstructorBean {
String name;
Integer id;
public NoArgsConstructorBean() {
}
public String getName() {
return this.name;
}
public Integer getId() {
return this.id;
}
public void setName(String name) {
this.name = name;
}
public void setId(Integer id) {
this.id = id;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof NoArgsConstructorBean)) {
return false;
} else {
NoArgsConstructorBean other = (NoArgsConstructorBean)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id != null) {
return false;
}
} else if (!this$id.equals(other$id)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof NoArgsConstructorBean;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.getName();
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $id = this.getId();
result = result * 59 + ($id == null ? 43 : $id.hashCode());
return result;
}
public String toString() {
return "NoArgsConstructorBean(name=" + this.getName() + ", id=" + this.getId() + ")";
}
}
通常@AllArgsConstructor和@NoArgsConstructor同时出现
因为增加了有参构造方法会覆盖掉默认的无参构造方法,后续使用框架里底层很多用到反射,反射又经常使用到无参构造方法,通常需要额外增加@NoArgsConstructor
xml文件中注册bean的方式
构造方法
无参构造(最常用)
因为默认提供的是无参构造,这个是最常用
NoArgsConstructorBean :
package com.cskaoyan.component;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 无参构造方法
* 默认包含无参的构造方法
*/
@Data
@NoArgsConstructor
public class NoArgsConstructorBean {
String name;
Integer id;
}
有参构造:
HasArgsConstructorBean :
package com.cskaoyan.component;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 有参构造方法
*/
@AllArgsConstructor //写了有参构造方法,会将默认的无参构造方法覆盖掉
@Data
public class HasArgsConstructorBean {
String name;
Integer id;
//public HasArgsConstructorBean(String name, Integer id) {
// this.name = name;
// this.id = id;
//}
}
工厂
通常是整合已有代码的时候,可以使用工厂注册组件(比如你写了一个工具类)
实例工厂:
静态工厂:
生命周期:
Spring中bean的生命周期
并不是所有的bean都会执行完所有的声明周期。
默认只会执行到1和2。之后这些需要满足一定的条件才能执行到。
6和9做的事情很类似,只不过和7、8之间有一个顺序关系。
默认是不会执行红框框中的生命周期的,只有实现了这些接口才会执行。
CustomBeanPostProcessor代码:
测试代码:
package com.cskaoyan.bean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class LifeCycleTest {
@Test
public void mytest1(){
//执行完这行代码就意味着组件可以使用了,也就是1-9的生命周期都执行完了
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//不需要取出组件,生命周期就已经开始
//关闭上下文后就会执行10、11生命周期
applicationContext.close();
}
}
执行结果:
可以利用生命周期的先后顺序写一些简单的业务逻辑。
作业:使用spring为项目里的所有类的所有方法增加
写几个不同的bean ,每个bean里包含不同的方法。
通过spring 使得调用任何一个实现方法之前和之后都增加一些内容输出。(通过PostBeanProcessor实现)
提示:生命周期 动态代理
提示:狸猫换太子
举个例子:比如有几个类Cat、Dog、Tiger,这几个类中分别有miao、wang、wuuu这几个方法,那么从容器中取出组件,调用这几个方法的时候(注意不是容器初始化时),miao、wang、wuuu这几个方法执行之前都会输出hello,执行之后都会输出world