新阶段的介绍:
课程的情况
Maven配置的注意事项:
举个例子:我把代码上传到qq群共享目录,大家下载代码下来看
大家下载了老师的代码,然后发现依赖出问题了。
疑问:
1、老师的依赖是上课用过的没问题
2、我自己的maven配置过了,是正确的配置
3、那么为什么依赖还出问题呢??
问题在.idea文件中:
.idea文件包含了idea应用的配置,也就包含了maven的配置。
如果你的配置是和老师不一致,实际上就会使用到默认的maven配置了。
其实就是打开项目的时候,引入了其他人的maven配置。
1、可以不选择.idea文件夹,自己重新生成目录结构 →可以在project-structure中重新标记;也可以选择maven配置文件重新引入。
2、检查maven配置,重新配置为自己本地的配置。
也要求大家去做一个other(new project) settings中的maven配置。
两种操作的步骤:
一、
二、
配置本地仓库:
由于我的本地仓库还是默认的(放在C:\Users\Administrator.m2\repository目录下),我决定对它重新配置一下。
将Settings、File—Other Settings—Setting for New Project的本地仓库都改成这样:
并去D:\developmer_tools\apache-maven-3.5.3\conf\settings.xml目录下将
改成了:
maven配置文件中的注意的点:
配置镜像
注意mirrors下的mirror,这配置的是远程仓库或者说镜像,我们配置的是阿里云的镜像,这就会比从中央仓库下载快很多。
profiles下的profile标签:指定jdk的版本,不指定的话IDEA可能会用其默认的版本(jdk1.5或者1.6)
如何查看你的项目使用jdk哪个版本进行编译的呢?
File—Project Structure
如果没有上述的配置,可以手动到modules这里来更改,但是如果你引进新的依赖,重新刷新pom文件的时候,会变回1.5或1.6
同时,不进行配置,这里也容易编程1.5或1.6
设计模式
1.1概述
MVC模式
模式——经验
软件开发实践过程中总结的经验。
代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。
MVC 模式
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
最终:
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
1.2设计模式需要注意的地方
经验——城市包围农村
不要乱用
要结合实际情况
设计模式不是万能的。
1.3优点
•代码质量好 可靠性高。
•复用代码。代码重用。
•代码更规范,更容易被人理解。
2软件设计的原则()
2.1什么是
设计所要解决的主要问题,是如何高效率、高质量、低风险的应对各种各类变化,例如需求变更、软件升级等。
2.2设计模式的五个原则solid
S - 单一职责原则:
O - 开放封闭原则:
L - Liskov原则:
I - 接口隔离原则:
D – 依赖倒置原则:
2.3 S - 单一职责原则
比如OrderServlet只负责订单模块,UserServlet只负责用户模块。
一个模块负责一个功能
一个类负责一个业务
一个API去实现一个功能
不同的功能分隔开。一个类如果需要变化,最好只能有一个维度的原因。
2.4 O - 开放封闭原则:
对什么开放:对扩展开放
扩展:新功能可以(提倡)去增加代码(增加一个类)
项目不断迭代(增加新的需求)
对什么封闭:对修改封闭
不建议去修改代码 (尤其是一些底层的API)
牵一发而动全身。
2.5 L - Liskov原则:
李氏替换原则:任何一个基类可以出现的地方,子类一定可以出现。
用写法一
//写法1
UserDao dao = new UserDaoImpl2();
dao.findUserByid();
dao.findUserByName();
//写法2
UserDaoImpl2 dao = new UserDaoImpl2 ();
dao.findUserByid();
dao.findUserByName();
2.6 I - 接口隔离原则:
接口:interface (功能)
隔离:分开
不同功能的接口,最好放在不同的interface里。
UserService中就定义和user相关的功能,ProductService中就定义和product相关的功能。
一个接口里最好不要集成太多的功能,
否则用户去实现该接口会比较麻烦。
2.7 D – 依赖倒置原则:(Spring DI IOC APSPECTJ)
依赖
具体依赖于抽象,而非抽象依赖于具体。
先去设计抽象的DAO (接口)
抽象和具体的关系
接口和抽象类 → 抽象
实现类 → 具体
从定义抽象的内容开始。先定义要做的事情是什么样子,然后在进行具体的实现。
通过接口的定义去实现应用程序中的功能。
Junit:
绿色的表示的就是单元测试的代码,绿色的可以使用蓝色的代码,蓝色的不可以使用绿色的代码。
这就使得正在编译的应用程序不会使用到单元测是的代码。
引入依赖:
pom.xml文件中新增:
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
如何看依赖是否正确的引入呢?
或者可以:右击Module—Open Moudle Settings
maven也可以帮助我们进行自动化测试。只需要写好对应的测试代码,maven就会帮助我们自动去运行。
你需要去做的就是在指定的目录下去编写测试代码。当执行mvn test指令时,会自动去到src/test/java目录下执行全部的单元测试用例。
为什么添加@Test注解就可以执行了?
其实里面就是利用了反射。
先写一个单元测试,DemoTest:
package com.cskaoyan.Singleton_Pattern;
import org.junit.Assert;
import org.junit.Test;
/**
* @author shihao
* @create 2020-07-13 18:02
*/
public class DemoTest {
@Test
public void mytest1() {
System.out.println("hello world");
String expected = "hello world";
String actual = "hello world";
Assert.assertEquals(expected, actual);
}
@Test
public void mytest2() {
System.out.println("hello world2");
}
@Test
public void mytest3() {
System.out.println("hello world3");
}
@Test
public void mytest4() {
String expected = "hello world";
UserService userService = new UserService();
String s = userService.sayHello();
Assert.assertEquals(expected, s); // maven test
}
}
@Before和@After的作用:
AnnotationTest:
@BeforeClass和@AfterClass的作用:
AnnotationTest2:
总结:
@Before和@After可以用于一些线程不安全的实例的初始化和资源释放
比如你在test1中写了一些代码,创建了几个对象,有的对象是线程不安全的,每次执行test1方法,都需要它给我们提供一个新的对象,就用@Before和@Afte。
@BeforeClass和@AfterClass可以用于线程安全的实例的初始化和资源释放
单例模式
•何为单例?单一实例。全局只有一个。
•哪些场合需要用到单例?
Servlet、ServletContext
从应用程序中取出的实例始终是同一个。 孤本
好处:节约资源
我们这个程序不会出现套娃现象,来举一个套娃的例子:
ProductService:
package com.cskaoyan.service;
public class ProductService {
ProductService service = new ProductService();
}
TaowaTest :
package com.cskaoyan;
import com.cskaoyan.service.ProductService;
import org.junit.Test;
public class TaowaTest {
@Test
public void mytest(){
ProductService productService = new ProductService();
}
}
这样就会报StackOverflowException。
而对ProductService 做如下修改就不会报错了:
因为静态成员变量只会执行一次。
package com.cskaoyan.service;
public class ProductService {
static ProductService service = new ProductService();
}
但是
public static Singleton2 getInstance(){
//singleton2 = new Singleton();
//判断singleton是否初始化,如果没有初始化,我们给他初始化
if (singleton2 == null){
singleton2 = new Singleton2();
}
return singleton2;
}
这段代码中的if判断并是线程不安全的,如果两个线程同时访问,发现singleton 不为空,就会new出两个不同的singleton 实例。
想改成线程安全的,只需要加一个锁即可。
但是加锁会影响执行效率。
public synchronized static Singleton2 getInstance(){
//singleton2 = new Singleton();
//判断singleton是否初始化,如果没有初始化,我们给他初始化
if (singleton2 == null){
singleton2 = new Singleton2();
}
return singleton2;
}
这两个代码都是懒加载(懒汉模式)
只不过第一个是线程不安全的懒加载,第二个是线程安全的懒加载。
懒加载和立即加载(懒汉模式和饿汉模式)
lazy loading
都是要去获得一个singleton的实例,getInstance方法来获得
1、懒加载 在调用getInstance方法的时候才初始化实例
2、立即加载 在还未调用getInstance方法的时候就已经完成了实例的初始化
立即加载:
Singleton3 :
package com.cskaoyan.singleton;
/**
* 立即加载
*/
public class Singleton3 {
private Singleton3() {
}
static Singleton3 singleton3 = new Singleton3();
public static Singleton3 getInstance() {
return singleton3;
}
}
Singleton4:
package com.cskaoyan.singleton;
/**
* 立即加载
* 这个是singleton3的一个变种
*/
public class Singleton4 {
private Singleton4() {
}
static Singleton4 singleton4;
//静态代码块儿
static {
singleton4 = new Singleton4();
}
public static Singleton4 getInstance() {
return singleton4;
}
}
可以用静态内部类来实现线程安全的懒加载,还没有效率问题。
outer:
package com.cskaoyan.singleton;
/**
* outer是外部类
* inner是内部类
*/
public class Outer {
/**
* 使用到静态内部类的方法
*/
public static void useInner() {
System.out.println("useInner");
Inner.innerMethod();
}
/**
* 没有使用静态内部类的方法
*/
public static void noUseInner() {
System.out.println("noUseInner");
}
static class Inner {
static {
System.out.println("inner static coding space");
}
public static void innerMethod() {
System.out.println("hello inner");
}
}
}
StaticInnerClassTest :
package com.cskaoyan;
import com.cskaoyan.singleton.Outer;
import org.junit.Test;
public class StaticInnerClassTest {
/**
* 没有使用到静态内部类的方法
*/
@Test
public void mytest1(){
Outer.noUseInner();
}
/**
* 使用到静态内部类的方法
*/
@Test
public void mytest2(){
Outer.useInner();
}
}
工厂模式
作用是生产实例,工厂要提供create方法。
通过工厂可以把实例的实例化细节隐藏起来。
XXXFactory的时候,就是获得xxx实例的。当看到类名是XXXFactory的时候,就要意识到这是一个工厂的设计模式。
简单工厂
通过给工厂的生产方法传入不同的内容,工厂生产的内容就有所区别。
可以发现简单工厂模式和开闭原则有些违背,于是就有了工厂方法模式。
工厂方法模式
新增代码来解决上述问题
通过定义工厂接口,由具体的工厂生产具体的实例。
工厂都具备create生产方法,但是方法的具体实现是不同的。
工厂的使用情况又分为两种情况:
a. 实例工厂:先要去获得工厂的实例,再调用createCar创建实例。
b. 静态工厂:直接调用生产实例的方法,不需要先去获得工厂实例。
代理模式:
先想一下什么是代理:
静态代理和动态代理:
动态代理
a. 第一种实现方式:jdk动态代理
jdk提供的api来获得代理对象(增强对象)
jdk动态代理使用要有接口的实现
HelloService :
package com.cskaoyan.service;
public interface HelloService {
public void sayHello();
public void method2();
}
HelloServiceImpl:
package com.cskaoyan.service;
public class HelloServiceImpl implements HelloService{
@Override
public void sayHello() {
System.out.println("hello world");
}
@Override
public void method2() {
System.out.println("method2");
}
}
ProxyTest:
package com.cskaoyan;
import com.cskaoyan.service.HelloService;
import com.cskaoyan.service.HelloServiceImpl;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理
*/
public class ProxyTest {
/**
* 没有使用动态代理
*/
@Test
public void mytest() {
//获得委托类对象
HelloService helloService = new HelloServiceImpl();
System.out.println("起床");
helloService.sayHello();
System.out.println("编程");
}
/**
* 使用jdk动态代理
*/
@Test
public void mytest2() {
//在hello world之前 起床
//hello world之后 编程
//先获得委托类对象的实例
HelloService helloService = new HelloServiceImpl();
//去获得一个代理对象来完成增强 → jdk动态代理获得增强对象(代理对象)
//classloader都是和委托类对象相关的
//interfaces都是和委托类对象相关的
HelloService helloServiceProxy = (HelloService) Proxy.newProxyInstance(HelloServiceImpl.class.getClassLoader(),
helloService.getClass().getInterfaces(), new InvocationHandler() {
//invoke中
//1.是要去执行委托类的方法
//2.可以去做增强
//返回值:Object 对应委托类方法执行的返回值
//参数:
// proxy :代理对象
// method: 委托类方法的method
// args: 委托类方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("起床");
//invoke方法中可以使用反射来执行委托类的方法
//第一个参数是委托类对象,而不是代理对象
Object invoke = method.invoke(helloService, args);
System.out.println("编程");
return invoke;
}
});
//helloServiceProxy.sayHello(); //使用代理对象去执行才会增强
//helloService.sayHello(); //使用委托对象只会输出hello world
helloServiceProxy.method2();
}
}
接口中的方法全部都被增强了。(不论是sayHello方法还是method2方法都被增强了)
用途:处理事务:比如service中的每一个方法都要增加事务,就可以利用代理增加开启事务的方法和提交并关闭事务的方法。
这是一个错误示范,因为jdk动态代理不能够使用实现类来接收代理对象
b. 第二种实现方式:cglib动态代理
不需要有接口的实现,cglib基于继承去实现的,proxy对象是继承委托类对象
代码写起来和jdk动态代理基本是一样,获得代理对象的过程使用api不同。
首先应当导包
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.2.12version>
dependency>
HelloService2 :
package com.cskaoyan.service;
/**
* 用cglib单元测试使用
* 这个类没有接口的实现
*/
public class HelloService2 {
public void sayHello2(){
System.out.println("hello world2");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void method2(){
System.out.println("method2");
}
}
CglibProxyTest :
package com.cskaoyan;
import com.cskaoyan.service.HelloService2;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import org.junit.Test;
import java.lang.reflect.Method;
/**
* cglib动态代理
*/
public class CglibProxyTest {
/**
* 计算helloService2中的方法的执行时间
*/
@Test
public void mytest1() {
//jdk没有提供,要导包
HelloService2 helloService2 = new HelloService2();
//第二个参数也是InvocationHandler,和jdk动态代理的InvocationHandler不同
//这个是net.sf.cglib.proxy.InvocationHandler,jdk中的是java.lang.reflect.InvocationHandler
HelloService2 helloService2Proxy = (HelloService2) Enhancer.create(HelloService2.class, new InvocationHandler() {
//invoke中的三个参数和jdk动态代理是一样的
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//对method2不做增强处理
if ("method2".equals(method.getName())) {
return method.invoke(helloService2, args);
}
long start = System.currentTimeMillis();
Object invoke = method.invoke(helloService2, args);
long end = System.currentTimeMillis();
long cost = end - start;
System.out.println(method.getName() + "执行时间是:" + cost);
return invoke;
}
});
helloService2Proxy.sayHello2();
helloService2Proxy.method2();
}
}
建造者模式:
也是生产实例,更侧重参数的设置
造人 → human
工厂模式是首先获得工厂实例,通过工厂实例的create方法才完成对象(animal)的实例化。
而建造者模式,当创建建造者(builder)的时候就已经完成了对象(human)的实例化。
Foot:
package com.cskaoyan.bean;
public class Foot {
Integer size;
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
@Override
public String toString() {
return "Foot{" +
"size=" + size +
'}';
}
}
Head:
package com.cskaoyan.bean;
public class Head {
Integer iq;
Integer eq;
String name;
public Integer getIq() {
return iq;
}
public void setIq(Integer iq) {
this.iq = iq;
}
public Integer getEq() {
return eq;
}
public void setEq(Integer eq) {
this.eq = eq;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Head{" +
"iq=" + iq +
", eq=" + eq +
", name='" + name + '\'' +
'}';
}
}
Leg:
package com.cskaoyan.bean;
public class Leg {
Integer length;
boolean strong;
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
public boolean isStrong() {
return strong;
}
public void setStrong(boolean strong) {
this.strong = strong;
}
@Override
public String toString() {
return "Leg{" +
"length=" + length +
", strong=" + strong +
'}';
}
}
Human:
package com.cskaoyan.bean;
public class Human {
Foot foot = new Foot();
Head head = new Head();
Leg leg = new Leg();
public Foot getFoot() {
return foot;
}
public void setFoot(Foot foot) {
this.foot = foot;
}
public Head getHead() {
return head;
}
public void setHead(Head head) {
this.head = head;
}
public Leg getLeg() {
return leg;
}
public void setLeg(Leg leg) {
this.leg = leg;
}
@Override
public String toString() {
return "Human{" +
"foot=" + foot +
", head=" + head +
", leg=" + leg +
'}';
}
}
注意这三行代码:
Foot foot = new Foot();
Head head = new Head();
Leg leg = new Leg();
先初始化出来,再在后面调用set和get方法,才能保证set和get到的是同一个Foot、Head或Leg对象。
HumanBuilder有两种写法(通常用第二种):
第一种写法:
package com.cskaoyan.builder;
import com.cskaoyan.bean.Human;
public class HumanBuilder2 {
//保证了new一个builder的时候同时new了一个human,就是空白的human,是这些set方法所共有的human
Human human = new Human();
//这一些方法要给同一个人来创建
//保证这一个方法中使用的是同一个human
public void setFootSize(Integer size) {
human.getFoot().setSize(size);
}
//需要保证都是同一个head
public void setHeadIq(Integer iq) {
human.getHead().setIq(iq);
}
public void setHeadEq(Integer eq) {
human.getHead().setEq(eq);
}
public void setHeadName(String name) {
human.getHead().setName(name);
}
public void setLegLength(Integer length) {
human.getLeg().setLength(length);
}
public void setLegStrong(boolean strong) {
human.getLeg().setStrong(strong);
}
public Human build() {
return human;
}
public Human getHuman() {
return human;
}
public void setHuman(Human human) {
this.human = human;
}
}
第二种写法:(链式法则)
package com.cskaoyan.builder;
import com.cskaoyan.bean.Human;
public class HumanBuilder {
//保证了new一个builder的时候同时new了一个human,就是空白的human,是这些set方法所共有的human
Human human = new Human();
//这一些方法要给同一个人来创建
//保证这一个方法中使用的是同一个human
public HumanBuilder setFootSize(Integer size) {
human.getFoot().setSize(size);
return this;
}
//需要保证都是同一个head
public HumanBuilder setHeadIq(Integer iq) {
human.getHead().setIq(iq);
return this;
}
public HumanBuilder setHeadEq(Integer eq) {
human.getHead().setEq(eq);
return this;
}
public HumanBuilder setHeadName(String name) {
human.getHead().setName(name);
return this;
}
public HumanBuilder setLegLength(Integer length) {
human.getLeg().setLength(length);
return this;
}
public HumanBuilder setLegStrong(boolean strong) {
human.getLeg().setStrong(strong);
return this;
}
public Human build() {
return human;
}
public Human getHuman() {
return human;
}
public void setHuman(Human human) {
this.human = human;
}
}
测试类:
package com.cskaoyan;
import com.cskaoyan.bean.Human;
import com.cskaoyan.builder.HumanBuilder;
import org.junit.Test;
public class MyTest {
@Test
public void mytest(){
HumanBuilder humanBuilder2 = new HumanBuilder();
humanBuilder2.setLegLength(120);
humanBuilder2.setHeadIq(150);
humanBuilder2.setHeadEq(150);
humanBuilder2.setHeadName("songge");
humanBuilder2.setFootSize(44);
humanBuilder2.setLegStrong(true);
Human human = humanBuilder2.build();
System.out.println(human);
}
@Test
public void mytest2(){
HumanBuilder humanBuilder = new HumanBuilder();
humanBuilder.setLegLength(120)
.setHeadIq(150)
.setHeadEq(150)
.setHeadName("songge")
.setFootSize(44)
.setLegStrong(true);
Human human = humanBuilder.build();
System.out.println(human);
}
}
作业:创建一个动态代理,动态代理对计算委托类方法的执行时间,并给输出到控制台中;该动态代理的对象在可以共同工厂获得
提示:动态代理和工厂结合