spring 相关问题

一、spring Aop相关内容

1.Spring 5.x 中 AOP 默认依旧使用 JDK 动态代理。
2.SpringBoot 2.x 开始,为了解决使用 JDK 动态代理可能导致的类型转化异常而默认使用 CGLIB。
3.在 SpringBoot 2.x 中,如果需要默认使用 JDK 动态代理可以通过配置项spring.aop.proxy-target-class=false来进行修改,proxyTargetClass配置已无效。

#proxyTargetClass = true 设置基于CGLIB 技术实现
#在application.properties文件中通过spring.aop.proxy-target-class来配置 通过jdk实现
spring.aop.proxy-target-class=false

springboot aop代理改变

二、spring 抽象类注入对象。

总结

第一,抽象类也可以使用spring注解完成对象的注入;
  第二,通常我们在一个类中注入对象时,会将该属性私有化,也就是使用private修饰,这样一来该属性会变成类的私有属性,也就是该属性只能在该类中被调用;显然,用到这里不合适,因为我们需要它的子类也能访问得到该属性,所以使用protected修饰。

spring 相关问题_第1张图片原文-java 抽象类使用@Autowrited注入对象,子类直接使用父类的该属性

三、spel

spring 相关问题_第2张图片

EvaluationContext接口

表示上下文环境,默认实现是org.springframework.expression.spel.support包中的StandardEvaluationContext类,使用setRootObject方法来设置根对象,使用setVariable方法来注册自定义变量,使用registerFunction来注册自定义函数等等。

Expression接口

表示表达式对象,默认实现是org.springframework.expression.spel.standard包中的SpelExpression,提供getValue方法用于获取表达式值,提供setValue方法用于设置对象值。

 @Test
    public void test1() {
    // 1.创建spelparser对象
        ExpressionParser parser = new SpelExpressionParser();
        //2.扫描 语句生成expression对象
        Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");
        //3.构造上下文对象,
        EvaluationContext context = new StandardEvaluationContext();
        // 设置值
        context.setVariable("end", "!");
        // 使用表达式对象根据上下文求值
        System.out.println(expression.getValue(context));
    }

1)创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现;

2)解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。

3)构造上下文:准备比如变量定义等等表达式需要的上下文数据。

4)求值:通过Expression接口的getValue方法根据上下文获得表达式值。

是不是很简单,接下来让我们看下其具体实现及原理吧。

3.1 变量定义 引用

变量定义及引用

变量定义通过EvaluationContext接口的setVariable(variableName, value)方法定义;在表达式中使用"#variableName"引用;除了引用自定义变量,SpE还允许引用根对象及当前上下文对象,使用"#root"引用根对象,使用"#this"引用当前上下文对象;

@Test
public void testVariableExpression() {
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("name", "路人甲java");
    context.setVariable("lesson", "Spring系列");
 
    //获取name变量,lesson变量
    String name = parser.parseExpression("#name").getValue(context, String.class);
    System.out.println(name);
    String lesson = parser.parseExpression("#lesson").getValue(context, String.class);
    System.out.println(lesson);
 
    //StandardEvaluationContext构造器传入root对象,可以通过#root来访问root对象
    context = new StandardEvaluationContext("我是root对象");
    String rootObj = parser.parseExpression("#root").getValue(context, String.class);
    System.out.println(rootObj);
 
    //#this用来访问当前上线文中的对象
    String thisObj = parser.parseExpression("#this").getValue(context, String.class);
    System.out.println(thisObj);
}
路人甲java
Spring系列
我是root对象
我是root对象

3.2对象属性存取及安全导航表达式

对象属性获取非常简单,即使用如“a.property.property”这种点缀式获取,SpEL对于属性名首字母是不区分大小写的;SpEL还引入了Groovy语言中的安全导航运算符“(对象|属性)?.属性”,用来避免“?.”前边的表达式为null时抛出空指针异常,而是返回null;修改对象属性值则可以通过赋值表达式或Expression接口的setValue方法修改。

public static class Car {
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                '}';
    }
}
 
public static class User {
    private Car car;
 
    public Car getCar() {
        return car;
    }
 
    public void setCar(Car car) {
        this.car = car;
    }
 
    @Override
    public String toString() {
        return "User{" +
                "car=" + car +
                '}';
    }
}
 
@Test
public void test5() {
    User user = new User();
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("user", user);
 
    ExpressionParser parser = new SpelExpressionParser();
    //使用.符号,访问user.car.name会报错,原因:user.car为空
    try {
        System.out.println(parser.parseExpression("#user.car.name").getValue(context, String.class));
    } catch (EvaluationException | ParseException e) {
        System.out.println("出错了:" + e.getMessage());
    }
    //使用安全访问符号?.,可以规避null错误
    System.out.println(parser.parseExpression("#user?.car?.name").getValue(context, String.class));
 
    Car car = new Car();
    car.setName("保时捷");
    user.setCar(car);
 
    System.out.println(parser.parseExpression("#user?.car?.toString()").getValue(context, String.class));
}
出错了:EL1007E: Property or field 'name' cannot be found on null
null
Car{name='保时捷'}

3.3 ExpressionParser接口

表达式解析接口。默认实现org.springframework.expression.spel.standard.SpelExpressionParser使用parseExpression方法将表达式字符串解析为Experssion对象,常用的API:

public interface ExpressionParser {
//该方法使用的ParserContext为null,即不使用模板
Expression parseExpression(String expressionString) throws ParseException;
//expressionString:待解析的字符串。context解析的上下文对象
Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}

3.3.1 ParserContext:表示解析的模板。

即ExpressionParser解析规则的字符串。例如org.springframework.expression.common.TemplateParserContext类,只是解析#{带解析的字符串}

案例一:不使用模板解析表达式

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); 
//现在的值message是“ Hello World!”。
String message = (String) exp.getValue();

案例二:使用模板解析表达式

ExpressionParser parser = new SpelExpressionParser();
//定义模板。默认是以#{开头,以}结尾
TemplateParserContext PARSER_CONTEXT = new TemplateParserContext();
//传入解析模板
Expression exp = parser.parseExpression("#{'Hello World'.concat('!')}",PARSER_CONTEXT);
String message = (String) exp.getValue();
System.out.println(message);


Test
public void testParserContext() {
    ExpressionParser parser = new SpelExpressionParser();
    ParserContext parserContext = new ParserContext() {
        @Override
        public boolean isTemplate() {
            return true;
        }

        @Override
        public String getExpressionPrefix() {
            return "#{";
        }

        @Override
        public String getExpressionSuffix() {
            return "}";
        }
    };
    String template = "#{'Hello '}#{'World!'}";
    Expression expression = parser.parseExpression(template, parserContext);
    System.out.println(expression.getValue());
}

在此我们演示的是使用ParserContext的情况,此处定义了ParserContext实现:定义表达式是模块,表达式前缀为“#{”,后缀为“}”;使用parseExpression解析时传入的模板必须以“#{”开头,以“}”结尾,如"#{'Hello '}#{‘World!’}"。

案例三:表达式不符合规则
1.未使用模板,但是传入#{}的字符串

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("#{'Hello World'}");
String message = (String) exp.getValue();
System.out.println(message);

异常

Exception in thread "main" org.springframework.expression.spel.SpelParseException: 
Expression [#{'Hello World'}] @1: EL1043E: Unexpected token. Expected 'identifier' but was 'lcurly({)'

2.使用#{}模板,但是传入%{}字符串

ExpressionParser parser = new SpelExpressionParser();
//定义模板
TemplateParserContext PARSER_CONTEXT = new TemplateParserContext();
//传入解析模板
Expression exp = parser.parseExpression("%{'Hello World'.concat('!')}", PARSER_CONTEXT);
String message = (String) exp.getValue();
System.out.println(message);
//没有进行解析
%{'Hello World'.concat('!')}

3.4 aop spel

Spring AOP 使用 SPEL 表达式记录日志
Spring Aop中解析spel表达式
spel-aop记录日志-掘金
如何通过aop+spel表达式玩转出不一样的切面实现–condition

spel 详细说明 推荐

spel 转转应用

你可能感兴趣的:(spring,spring,java,后端)