单元测试、反射、注解、动态代理 课程安排
单元测试
单元测试概述
单元测试
- 单元测试就是针对最小的功能单元编写测试代码,Java 程序最小的功能单元是方法,因此,单元测试就是针对 Java 方法的测试,进而检查方法的正确性。
目前测试方法是怎么进行的,存在什么问题
- 只有一个 main 方法,如果一个方法的测试失败了,其他方法测试会受到影响。
- 无法得到测试的结果报告,需要程序员自己去观察测试是否成功。
- 无法实现自动化测试。
JUnit 单元测试框架
- JUnit 是使用 Java 语言实现的单元测试框架,它是开源的,Java 开发者都应当学习并使用 JUnit 编写单元测试。
- 此外,几乎所用的 IDE 工具都集成了 JUnit,这样我们就可以直接在 IDE 中编写并运行 JUint 测试,JUnit 目前最先版本是 5。
Junit 优点
- JUnit 可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
- JUnit 可以生成全部方法的测试报告。
总结
- JUnit 单元测试是做什么的?
- JUnit 单元测试的优点是什么?
- JUnit 可以选择执行哪些测试方法,可以一键执行全部测试方法的测试。
- JUnit 可以生成测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
- 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
单元测试快速入门
步骤 单元测试快速入门
需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
分析:
-
将 JUint 的 jar 包导入到项目中
-
编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。 -
在测试方法上使用 @Test 注解:标注该方法是一个测试方法。 -
在测试方法中完成被测试方法的预期正确性测试。 -
选中测试方法,选中 “JUnit 运行”,如果 测试良好则是绿色;如果测试失败,则是红色。
业务方法
public class UserService {
public String loginName(String loginName,String password){
if ("admin".equals(loginName) && "123456".equals(password)){
return "登录成功";
}else {
return "用户名或者密码有问题";
}
}
public void selectNames(){
System.out.println(10/0);
System.out.println("查询全部用户名称成功~~~");
}
}
测试类
public class TestUserService {
@Test
public void testLoginName(){
UserService userService = new UserService();
String rs = userService.loginName("admin","123456");
Assert.assertEquals("您的登录业务可能出现问题","登录成功",rs);
}
@Test
public void testSelectNames(){
UserService userService = new UserService();
userService.selectNames();
}
}
总结
- JUnit 单元测试的实现过程是什么样的?
- 必须导入 JUnit 框架的 jar 包。
- 定义的测试方法必须是无参数无返回值,且公开的方法。
- 测试方法使用 @Test 注解标记。
- JUint 测试某个方法,测试全部方法怎么处理?成功的标志是什么?
- 测试某个方法直接右键该方法启动测试。
- 测试全部方法,可以选择类或者模块启动。
- 红色失败,绿色成功。
单元测试常用注解
JUnit 常用注解(JUnit 4.xxxx版本)
- 开始执行的方法:初始化资源。
- 执行完毕之后的方法:释放资源。
public class TestUserService {
@Before
public void before(){
System.out.println("======before方法执行一次======");
}
@After
public void after(){
System.out.println("======after方法执行一次======");
}
@BeforeClass
public static void beforeClass(){
System.out.println("======beforeClass方法执行一次======");
}
@AfterClass
public static void afterClass(){
System.out.println("======afterClass方法执行一次======");
}
@Test
public void testLoginName(){
UserService userService = new UserService();
String rs = userService.loginName("admin","123456");
Assert.assertEquals("您的登录业务可能出现问题","登录成功",rs);
}
@Test
public void testSelectNames(){
UserService userService = new UserService();
userService.selectNames();
}
}
JUnit 常用注解(JUnit 5.xxxx 版本)
- 开始执行的方法:初始化资源。
- 执行完毕之后的方法:释放资源。
使用与(JUnit 4.xxxx版本) 一样,只是名字换了。
反射
反射概述
反射概述
- 反射是指对于任何一个 Class 类,在“运行的时候”都可以直接得到这个类全部部分。
- 在运行时,可以直接得到这个类的构造器对象:Constructor
- 在运行时,可以直接得到这个类的成员变量对象:Field
- 在运行时,可以直接得到这个类的成员方法对象:Method
- 这种运行时动态获取类信息以及动态调用类中成分的能力称为 Java 语言的反射机制。
反射的关键:
- 反射的第一步都是先得到编译后的 Class 类对象,然后就可以得到 Class 的全部成分。
|
总结
- 反射的基本作用、关键?
- 反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分。
- 反射的核心思想和关键就是:得到编译以后的 class 文件对象。
反射获取类对象
反射的第一步:获取 Class 类的对象
Student
public class Student {
}
Test
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.zwzl.d2_reflect_class.Student");
System.out.println(c);
Class c1 = Student.class;
System.out.println(c1);
Student s = new Student();
Class c2 = s.getClass();
System.out.println(c2);
}
}
总结
- 反射的第一步是什么?
- 获取 Class 类对象,如此才可以解析类的全部成分
- 获取 Class 类的对象三种方式
- 方式一:Class c1 = Class.forName(“全类名”);
- 方式二:Class c2 = 类名.class;
- 方式三:Class c3 = 对象.getClass();
反射获取构造器对象
使用反射技术获取构造器对象并使用
使用反射技术获取构造器对象并使用
Student
public class Student {
private String name;
private int age;
private Student() {
System.out.println("无参数构造器执行!");
}
public Student(String name, int age) {
System.out.println("有参数构造器执行!");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
TestStudent1
public class TestStudent1 {
@Test
public void getConstructors(){
Class c = Student.class;
Constructor[] constructors = c.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}
@Test
public void getDeclaredConstructors(){
Class c = Student.class;
Constructor[] constructors = c.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}
@Test
public void getConstructor() throws Exception {
Class c = Student.class;
Constructor constructor = c.getConstructor();
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
Constructor constructor1 = c.getConstructor(String.class, int.class);
System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
}
@Test
public void getDeclaredConstructor() throws Exception {
Class c = Student.class;
Constructor constructor = c.getDeclaredConstructor();
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
Constructor constructor1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
}
}
使用反射技术获取构造器对象并使用
Constructor 类中用于创建对象的方法
public class TestStudent2 {
@Test
public void getDeclaredConstructor() throws Exception {
Class c = Student.class;
Constructor constructor = c.getDeclaredConstructor();
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
constructor.setAccessible(true);
Student s = (Student) constructor.newInstance();
System.out.println(s);
System.out.println("-------------------------------------");
Constructor constructor1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
Student s1 = (Student) constructor1.newInstance("张三", 35);
System.out.println(s1);
}
}
总结
- 利用反射技术获取构造器对象的方式
- getDeclaredConstructors()
- getDeclaredConstructor(Class<?>… parameterTypes)
- 反射得到的构造器可以做什么?
- 依然是创建对象的
- public newInstance(Object… initargs)
- 如果是非 public 的构造器,需要打开权限(暴力反射),然后再创建对象
- setAccessible(boolean)
- 反射可以破坏封装性,私有的也可以执行了。
反射获取成员变量对象
使用反射技术获取成员变量对象并使用
使用反射技术获取成员变量对象并使用
Student
public class Student {
private String name;
private int age;
public static String schoolName;
public static final String COUNTTPY = "中国";
public Student() {
System.out.println("无参数构造器执行!");
}
public Student(String name, int age) {
System.out.println("有参数构造器执行!");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
FieldTest1
public class FieldTest1 {
@Test
public void getDeclaredFields(){
Class c = Student.class;
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + "====>" + field.getType());
}
}
@Test
public void getDeclaredField() throws Exception {
Class c = Student.class;
Field field = c.getDeclaredField("name");
System.out.println(field.getName() + "===>" + field.getType());
}
@Test
public void getFields(){
Class c = Student.class;
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field.getName() + "===>" + field.getType());
}
}
@Test
public void getField() throws Exception {
Class c = Student.class;
Field field = c.getField("schoolName");
System.out.println(field.getName() + "===>" + field.getType());
}
}
使用反射技术获取成员变量对象并使用
Field 类中用于取值、赋值的方法
public class FieldTest2 {
@Test
public void setField() throws Exception{
Class c = Student.class;
Field field = c.getDeclaredField("name");
field.setAccessible(true);
Student s = new Student();
field.set(s,"张三");
System.out.println(s);
String name = (String) field.get(s);
System.out.println(name);
}
}
总结
- 利用反射技术获取成员变量的方式
- 获取类中成员变量对象的方法
- getDeclaredFields()
- getDeclaredField(String name)
- 反射得到成员变量可以做什么?
- 依然是某个对象中取值和赋值。
- void set(Object obj, Object value)
- Object get(Object obj)
- 如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值。
反射获取方法对象
使用反射技术获取方法对象并使用
使用反射技术获取方法对象并使用
使用发射技术获取方法对象并使用
Method 类中用于触发执行的方法
dog
public class Dog {
private String name;
public Dog() {
}
public Dog(String name) {
this.name = name;
}
public void run(){
System.out.println("狗跑的贼快~~~");
}
private void eat(){
System.out.println("狗吃骨头");
}
private String eat(String name){
System.out.println("狗吃" + name);
return "吃的很开心";
}
public static void inAddr(){
System.out.println("在吉山区有一群单身狗!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MethodTest1
public class MethodTest1 {
@Test
public void getDeclaredMethods(){
Class c = Dog.class;
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + ",返回类型:" + method.getReturnType() + ",个数:" + method.getParameterCount());
}
}
@Test
public void getDeclaredMethod() throws Exception {
Class c = Dog.class;
Method method = c.getDeclaredMethod("eat");
Method method1 = c.getDeclaredMethod("eat",String.class);
method.setAccessible(true);
method1.setAccessible(true);
Dog dog = new Dog();
Object rs = method.invoke(dog);
System.out.println(rs);
Object rs1 = method1.invoke(dog, "骨头");
System.out.println(rs1);
}
@Test
public void getMethods(){
Class c = Dog.class;
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method.getName() + ",返回类型:" + method.getReturnType() + ",个数:" + method.getParameterCount());
}
}
@Test
public void getMethod() throws Exception {
Class c = Dog.class;
Method method = c.getMethod("run");
System.out.println(method.getName() + ",返回类型:" + method.getReturnType() + ",个数:" + method.getParameterCount());
Method method1 = c.getMethod("eat",String.class);
System.out.println(method1.getName() + ",返回类型:" + method1.getReturnType() + ",个数:" + method1.getParameterCount());
}
}
总结
- 利用反射技术获取成员对象的方法
- 获取类中成员方法对象
- getDeclaredMethods()
- getDeclaredMethodString name, Class<?>… parameterTypes)
- 反射得到成员方法可以做什么?
- 依然是在某个对象中触发该方法执行。
- Object invoke(Object obj, Object… args)
- 如果某成员方法是非 public 的,需要打开权限(暴力反射),然后再触发执行
反射的作用-绕过编译阶段为集合添加数据
反射的作用-绕过编译阶段为集合添加数据
public class ReflectTet {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
System.out.println(list1.getClass());
System.out.println(list2.getClass());
System.out.println(list1.getClass() == list2.getClass());
System.out.println("-----------------------------------");
ArrayList<Integer> list3 = new ArrayList<>();
list3.add(15);
list3.add(85);
Class c = list3.getClass();
Method method = c.getDeclaredMethod("add", Object.class);
boolean rs = (boolean)method.invoke(list3,"张三");
System.out.println(rs);
System.out.println(list3);
ArrayList list4 = list1;
list4.add("asdfsd");
System.out.println(list4);
}
}
总结
- 反射为何可以给约定了泛型的集合存入其他类型的元素?
- 编译成Class文件进入运行阶段的时候,泛型会自动擦除。
- 反射是作用在运行时的技术,此时已经不存在泛型了。
反射的作用-通用框架的底层原理
案例 反射做通用框架
需求:给你任意一个对象,在不清楚对象字段的情况,可以把对象的字段名称和对应值存储到文件中去。
分析:
- 定义一个方法,可以接收任意类的对象。
- 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
- 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
- 使用反射获取对象的 Class 类对象,然后获取全部成员变量信息。
- 遍历成员变量信息,然后提取本成员变量在对象中的具体值。
- 存入成员变量名称和值到文件中去即可。
实体类:Student
public class Student {
private String name;
private char sex;
private int age;
private String className;
private String hobby;
public Student() {
}
public Student(String name, char sex, int age, String className, String hobby) {
this.name = name;
this.sex = sex;
this.age = age;
this.className = className;
this.hobby = hobby;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
实体类:Teacher
public class Teacher {
private String name;
private char sex;
private double salary;
public Teacher() {
}
public Teacher(String name, char sex, double salary) {
this.name = name;
this.sex = sex;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
工具类:MybatisUtil
public class MybatisUtil {
public static void save(Object obj){
try (
PrintStream ps = new PrintStream(new FileOutputStream("junit-reflect-annotation-proxy-app/src/data.txt",true))
){
Class c = obj.getClass();
ps.println("=====================" + c.getSimpleName() + "=====================");
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
String name = field.getName();
field.setAccessible(true);
String value = field.get(obj) + "";
ps.println(name + "=" + value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
主程序:ReflectTest
public class ReflectTest {
public static void main(String[] args) {
Student s = new Student();
s.setName("张三");
s.setAge(22);
s.setSex('男');
s.setClassName("大四");
s.setHobby("学习");
MybatisUtil.save(s);
Teacher t = new Teacher();
t.setName("老师");
t.setSex('男');
t.setSalary(10000);
MybatisUtil.save(t);
}
}
总结
- 反射的作用?
- 可以在运行时得到一个类的全部成分然后操作。
- 也可以破坏封装性。(很突出)
- 也可以破坏泛型的约束性。(很突出)
- 更重要的用途是适合:做Java高级框架
注解
注解的概述
注解概述、作用
注解的作用是什么呢?
- 对 Java 中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理有业务需求来决定。
- 例如:JUint 框架中,标记了注解 @Test 的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。
总结
- 注解的作用
- 对 Java 中类、方法、成员变量做标记,然后进行特殊处理。
- 例如:JUint 框架中,标记了注解 @Test 的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行
自定义注解
自定义注解—格式
-
自定义注解就是自己做一个注解来使用。
自定义注解:MyBook
public @interface MyBook {
String name();
String[] authors();
double price();
}
主程序:AnnotationTest1
@MyBook(name = "《精通JavaSE》",authors = {"自我自律","zwzl"},price = 100)
public class AnnotationTest1 {
@MyBook(name = "《精通JavaSE》",authors = {"自我自律","zwzl"},price = 100)
private AnnotationTest1(){
}
@MyBook(name = "《精通JavaSE1》",authors = {"自我自律","zwzl"},price = 100)
public static void main(String[] args) {
@MyBook(name = "《精通JavaSE2》",authors = {"自我自律","zwzl"},price = 100)
int age = 21;
}
}
特殊属性
- value 属性,如果只有一个 value 属性的情况下,使用 value 属性的时候可以省略 value 名称 不写!
- 但是如果有多个属性,且多个属性没有默认值,那么 value 名称是不能省略的。
总结
-
自定义注解
元注解
元注解
元注解有两个:
- @Target:约束自定义注解只能在哪些地方使用。
- @retention:申明注解的生命周期
MyTest
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
AnnotationTest2
public class AnnotationTest2 {
@MyTest
private String name;
@MyTest
public void test(){
}
public static void main(String[] args) {
}
}
@Target 中可使用的值定义在 ElementType 枚举类中,常用值如下
- TYPE:类、接口
- FIELD:成员变量
- METHOD:成员方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造器
- LOCAL_VARIABLE:局部变量
@Retention 中可使用的值定义在 RetentionPolicy 枚举类汇总,常用值如下
- SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值。
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
总结
- 元注解是什么?
- 注解注解的注解
- @Target 约束自定义注解可以标记的范围。
- @Rentention 用来约束自定义注解的存活范围。
注解解析
注解的解析
- 注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
与注解解析相关的接口
-
Annotation:注解的顶级接口,注解都是 Annotation 类型的对象 -
AnnotatedElement:该接口定义了注解解析相关的解析方法 -
所有的类成分 Class,Method,Filed,Constructor,都实现了 AnnotatedElement 接口他们都拥有解析注解的能力。
解析注解的技巧
- 注解在哪个成分上,我们就先拿哪个成分对象。
- 比如注解作用成员方法,则要获得该成员方法对应的 Method 对象,再来拿上面的注解
- 比如注解作用在类上,则要该类的 Class 对象,再来拿上面的注解
- 比如注解作用在成员变量上,则要获得该成员变量对应的 Field 对象,再来拿上面的注解
案例 注解解析的案例
需求:注解解析的案例
分析:
- 定义注解 Book,要求如下:
- 包含属性:String value() 书名
- 包含属性:double price() 价格,默认值为 100
- 包含属性:String[] authors() 多位作者
- 限制注解使用的位置:类和成员方法上
- 指定注解的有效范围:RUNTIME
- 定义 BookStore 类,在类和成员方法上使用 Book 注解
- 定义 AnnotationTest1 测试类获取 Book 注解上的数据
NewBook:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NewBook {
String value();
double price() default 100;
String[] author();
}
AnnotationTest3 和 BookStore:
public class AnnotationTest3 {
@Test
public void parseClass(){
Class c = BookStore.class;
if (c.isAnnotationPresent(NewBook.class)){
NewBook newBook = (NewBook) c.getDeclaredAnnotation(NewBook.class);
System.out.println(newBook.value());
System.out.println(newBook.price());
System.out.println(Arrays.toString(newBook.author()));
}
}
@Test
public void parseMethod() throws Exception {
Class c = BookStore.class;
Method method = c.getDeclaredMethod("test");
if (method.isAnnotationPresent(NewBook.class)){
NewBook newBook = method.getDeclaredAnnotation(NewBook.class);
System.out.println(newBook.value());
System.out.println(newBook.price());
System.out.println(Arrays.toString(newBook.author()));
}
}
}
@NewBook(value = "《法外狂徒张三》",price = 100,author = {"张三","罗老师"})
class BookStore{
@NewBook(value = "《三少爷的剑》",price = 66,author = {"古龙","熊耀华"})
public void test(){
}
}
总结
-
注解解析的方式
注解的应用场景一:JUnit框架
案例 模拟JUnit框架
需求:
- 定义若干个方法,只要加了 @MyTest 注解,就可以在启动时被触发执行
分析:
- 定义一个自定义注解 @MyTest,只能注解方法,存活范围是一直都在。
- 定义若干个方法,只要有 @MyTest 注解的方法就能在启动时被触发执行,没有这个主机的方法不能执行。
MyTest:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
AnnotationTest4
public class AnnotationTest4 {
@MyTest
public void test1(){
System.out.println("=================test1执行=================");
}
public void test2(){
System.out.println("=================test2执行=================");
}
@MyTest
public void test3(){
System.out.println("=================test3执行=================");
}
public static void main(String[] args) throws Exception {
AnnotationTest4 t = new AnnotationTest4();
Class c = AnnotationTest4.class;
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyTest.class)){
method.invoke(t);
}
}
}
}
动态代理
动态代理概述、快速入门
什么是代理?
- 代理指:某些场景下对象会找一个代理对象,来辅助自己完成一些工作,如:歌星(经纪人),买房的人(房产中介)。
代理主要干什么,他是如何工作的?
代理主要是针对对象的行为额外做一些辅助操作。
如何创建代理对象
接口:Skill
public interface Skill {
void jump();
void sing();
}
实体类:Star
public class Star implements Skill{
private String name;
public Star() {
}
public Star(String name) {
this.name = name;
}
@Override
public void jump() {
System.out.println(name + "开始跳舞!");
}
@Override
public void sing() {
System.out.println(name + "开始唱歌!");
}
}
代理类:StarAgentProxy
public class StarAgentProxy {
public static Skill getProxy(Star s){
return (Skill) Proxy.newProxyInstance(s.getClass().getClassLoader(),
s.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收首付款……");
Object rs = method.invoke(s, args);
System.out.println("收尾款,把杨超月接回来……");
return rs;
}
});
}
}
主程序:Test
public class Test {
public static void main(String[] args) {
Star star = new Star("杨超月");
Skill skill = StarAgentProxy.getProxy(star);
skill.jump();
skill.sing();
}
}
Java 中如何生成代理,并指定代理干什么事
总结
-
代理是什么?
- 一个对象,用来对被代理对象的行为额外做一些辅助工作。
-
在 Java 中实现动态代理的步骤是什么样的?
-
通过代理对象调用方法,执行流程是什么样的?
- 先走向代理
- 代理可以为方法额外做一些辅助工作。
- 开发真正触发对象的方法的执行。
- 回到代理中,有代理负责返回结果给方法的调用者。
动态代理的应用案例:做性能分析、代理的好处小结
案例 模拟企业业务功能开发,并完成每个功能的性能统计
需求:
- 模拟企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时。
分析:
- 定义一个 UserService 表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。
- 定义一个实现类 UserServiceImpl 实现 UserService ,并完成相关功能,且统计每个功能的耗时。
- 定义测试列,创建实现类对象,调用方法。
本案例存在哪些问题?
- 业务对象的每个方法都要进行性能统计,存在大量重复的代码。
- 解决方案,使用动态代理
接口:UserService
public interface UserService {
String login(String loginName,String passWord);
void deleteUsers();
String selectUsers();
}
实现类:UserServiceImpl
public class UserServiceImpl implements UserService{
@Override
public String login(String loginName, String passWord) {
String rs = "登录名和密码错误!";
if ("admin".equals(loginName) && "123456".equals(passWord)){
rs = "登录成功!";
}
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
return rs;
}
@Override
public void deleteUsers() {
try {
System.out.println("您正在删除用户数据中……");
Thread.sleep(2500);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public String selectUsers() {
String rs = "查询了10000个用户数据!";
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
return rs;
}
}
代理工具类:ProxyUtil
public class ProxyUtil {
public static <T> T getProxy(T userService){
return (T) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object rs = method.invoke(userService, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) / 1000.0 + "s");
return rs;
}
});
}
}
主程序:Test
public class Test {
public static void main(String[] args) {
UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
System.out.println(userService.login("admin","123456"));
System.out.println(userService.selectUsers());
userService.deleteUsers();
}
}
优化的关键步骤
-
必须有接口,实现类要实现接口(代理通常是基于接口实现的)。 -
创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。
动态代理的优点
- 可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。
- 简化了编程工作、提高了开发效率,同时提高了软件系统的可扩展性。
- 可以为被代理对象的所有方法做代理。
- 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接口本身做代理。
|