Java 五种方法实现普通类注入spring管理的service、repository等资源

目录

  • 场景
  • 数据准备
    • 数据库
    • Entity
    • Repository
    • Service
    • Controller
  • 解决方法
    • 一、利用@PostConstruct
    • 二、利用ENUM枚举类
    • 三、利用ApplicationContextAware
    • 四、利用Hibernate Interceptor
    • 五、利用AOP Injection
  • 参考:

场景

在一些普通的工具类或者POJO类需要注入service或者repository等spring管理的资源进行一定的逻辑处理,普通类不受spring管理,所以利用@Autowired实现不了。本文介绍几种方式解决此问题。

本文利用老师与学生的关系作为例子讲解,一个老师可以管理多个学生,在查询老师列表时也将他名下的学生查询出来,Teacher表与Student不进行关联表处理,那么就需要在Teacher的Entity里注入StudentService进行查询。

数据准备

数据库

Student

 uuid | deleted |  status  | teacher 
------+---------+----------+---------
 s1   |         | active   | t1
 s2   | deleted | inactive | t1
(2 rows)

Teacher

 uuid 
------
 t1
 t2
(2 rows)

Entity

Teacher
不同的实现方法有不同的注入,具体的参考下文
Student

@Data
@Entity
@Table(name = "student")
@EqualsAndHashCode(of = {"id"})
public class Student {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "deleted")
	private String deleted;
	
	@Column(name = "status")
	private String status;
	
	@Column(name = "teacher")
	private String teacher;
}

Repository

StudentReposity

@Repository
public interface StudentReposity extends CrudRepository<Student, String>{
	
	@Query("select s from Student s where s.teacher=:teacherId")
	public Set<Student> findStudentsByTeacher(@Param("teacherId") String teacherId);
}

TeacherRepository

@Repository
public interface TeacherRepository extends CrudRepository<Teacher, String>{
	@Query("select t from Teacher t")
	public Set<Teacher> findTeachers();
}

Service

StudentService

@Service
public class StudentService {

	@Autowired
	StudentReposity studentReposity;
	
	public Set<Student> findStudentsByTeacher(String teacherId){
		return studentReposity.findStudentsByTeacher(teacherId);
	}
}

TeacherService

@Service
public class TeacherService {
	@Autowired
	TeacherRepository teacherRepository;
	
	public Set<Teacher> findTeachers(){
		return teacherRepository.findTeachers();
	}
}

Controller

@RestController
public class TeacherController {

	@Autowired
	TeacherService teacherService;
	
	@GetMapping("/teachers")
	public Set<Teacher> findTeachers(){
		return teacherService.findTeachers();
	}
}

解决方法

一、利用@PostConstruct

这种方法比较简单,但是如果你有多个Entity需要注入多个service的时候就比较麻烦一点,另外sonarqube代码检测也会有警告。
Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Transient
	private Set<Student> students;
	
	public Set<Student> getStudents(){
		return SpringBeanUtil.staticStudentService.findStudentsByTeacher(getId());
	}
}

利用@PostConstruct

@Component
public class SpringBeanUtil {
	@Autowired
	StudentService studentService;
	
	public static StudentService staticStudentService;
	
	@PostConstruct
	public void init() {
		staticStudentService = studentService;
	}
}

二、利用ENUM枚举类

Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;

	@Transient
	private Set<Student> students;
	
	public Set<Student> getStudents(){
		return EnumBeanUtil.BEAN.getBean(StudentService.class).findStudentsByTeacher(getId());
	}
}

定义获取ApplicatinContext的Enum

public enum EnumBeanUtil {

	BEAN;
	
	private ApplicationContext applicationContext;

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
	
	public <T> T getBean(Class<T> clazz) {
		return getApplicationContext().getBean(clazz);
	}
}

设置ENUM的applicationContext变量

@SpringBootApplication
public class Application extends SpringBootServletInitializer  {
	
    public static void main(String[] args) {
        EnumBeanUtil.BEAN.setApplicationContext(SpringApplication.run(Application.class, args));
    }

}

启动测试,可以得到studnets。

三、利用ApplicationContextAware

Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Transient
	private Set<Student> students;
	
	public Set<Student> getStudents(){
		StudentService studentService = BeanUtil.getBean(StudentService.class);
		return studentService.findStudentsByTeacher(getId());
	}
}

1、实现ApplicationContextAware接口

@Component
public class BeanUtil implements ApplicationContextAware{

    //@note
	private static ApplicationContext context;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		BeanUtil.context = applicationContext;
	}
	
	public static <T> T getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }

}

@note: 如果你的代码有sonarqube检查的话会有critical的警告,所以如果你有严格的代码规范的话这种方法不推荐。

测试

发起/teachers请求可得到结果:

[
    {
        "id": "t1",
        "students": [
            {
                "id": "s1",
                "deleted": null,
                "status": "active",
                "teacher": "t1"
            },
            {
                "id": "s2",
                "deleted": "deleted",
                "status": "inactive",
                "teacher": "t1"
            }
        ]
    },
    {
        "id": "t2",
        "students": []
    }
]

可以看到students查出来,证明StudnetService确实被注入进去了。

四、利用Hibernate Interceptor

Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;

	@Transient
	private Set<Student> students;
	
	@Transient
	@JsonIgnore
	private StudentService studentService;
	
	public Set<Student> getStudents(){
		return studentService.findStudentsByTeacher(getId());
	}
}

1、创建自己的Interceptor并扩展EmptyInterceptor

public class StudentInterceptor extends EmptyInterceptor{

	/**
	 * 
	 */
	private static final long serialVersionUID = -4771865788342461458L;

	@Autowired
	@Lazy //@note1
	StudentService studentService;
	
	//@note2
	@Override
	public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
	    //@note3
		if(entity instanceof Teacher) {
			((Teacher) entity).setStudentService(studentService);
			return true;
		}
		return false;
	}
}

@note1: 懒加载防止启动的时候出现bean加载死循环
@note2:重写onLoad方法,在对象加载的时候就注入service。
@note3: 对象是Teacher的注入studentService。如果对象多可以在这里添加自己的需求。

2、实例化自定义的Interceptor
如果是注解方式配置的话:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory"
						, basePackages = { "***.your.repository" })
public class PersistenceConfiguration {

	@Primary
	@Bean(name = "entityManagerFactory")
	public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
			@Qualifier("dataSource") DataSource dataSource) {
		//@note
		Map<String, Object> jpaProperties = new HashMap<>();
		jpaProperties.put("hibernate.ejb.interceptor", studentInterceptor());
		return builder.properties(jpaProperties)
		//your other configuration
		.build();
	}
	
	@Bean
	public StudentInterceptor studentInterceptor() {
		return new StudentInterceptor();
	}
    
    //other configuration
}

如果你的Spring boot版本号小于2,可以通过extends HibernateJpaAutoConfiguration 来实现,具体做法参考link.其他做法也可以参考该文章,但是我本人测都实现不了。

测试

发起/teachers请求可得到结果:

[
    {
        "id": "t1",
        "students": [
            {
                "id": "s1",
                "deleted": null,
                "status": "active",
                "teacher": "t1"
            },
            {
                "id": "s2",
                "deleted": "deleted",
                "status": "inactive",
                "teacher": "t1"
            }
        ]
    },
    {
        "id": "t2",
        "students": []
    }
]

可以看到students查出来,证明StudnetService确实被注入进去了。

五、利用AOP Injection

Entity
声明注解@Configurable、@Aspect。

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
@Configurable
@Aspect
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Transient
	private Set<Student> students;
	
	@Transient
	@JsonIgnore
	@Autowired
	private StudentService studentService;
	
	public Set<Student> getStudents(){
		return studentService.findStudentsByTeacher(getId());
	}
}

1、告诉spring激活aop injection

@SpringBootApplication
@EnableSpringConfigured  //@note。通知spring
public class Application extends SpringBootServletInitializer  {
	
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

2、使用AspectJ “Java Agent”
如果你是使用Eclipse,可以在Debug Configurations->Arguments->VM arguments添加==-javaagent:\your\path\to\aspectjweaver.jar==。需要下载aspectjweaver依赖。
如果你是直接打好包,则可执行java -jar your.jar -javaagent:\your\path\to\aspectjweaver.jar。

如果你是xml配置的话可以参考这篇文章.

要实现AOP可能还需要导入一些依赖:



    org.springframework.boot
    spring-boot-starter-aop
    2.2.6.RELEASE

如果你还有其他方法,欢迎讨论*-*

参考:

https://blog.csdn.net/qq_39056805/article/details/80285915

https://blog.csdn.net/Mrs_chens/article/details/93741730

https://stackoverflow.com/questions/9666922/enable-spring-aop-or-aspectj

https://dzone.com/articles/inject-spring-beans-aspectj

https://stackoverflow.com/questions/27744287/what-is-the-spring-java-config-equivalent-of-contextspring-configured

https://stackoverflow.com/questions/10008714/requested-bean-is-currently-in-creation-is-there-an-unresolvable-circular-refer

https://stackoverflow.com/questions/25283767/how-to-use-spring-managed-hibernate-interceptors-in-spring-boot/29919009

http://jblewitt.com/blog/?p=129

你可能感兴趣的:(java)