我目前正在研究Vaadin spring应用程序。我唯一能说的是,用户的身份验证/授权必须通过$visitor_ip = getUserIP();
if(!isset($db)) $db = connect_db(); // connecting to the databaste with PDO
$userName = $_SESSION['username'];
$query = "SELECT entries from visitorsCounter WHERE ip=:ip";
$stmt = $db->prepare ( $query );
$stmt->bindParam ( ":ip", $visitor_ip);
$stmt->execute ();
$result = $stmt->fetch ( PDO::FETCH_ASSOC );
if($result != null){ // **** if there isn't a row with that IP ****
$addEntryQuery = "UPDATE visitorsCounter SET entries = entries + 1 WHERE ip=:ip";
$stmt = $db->prepare ( $addEntryQuery );
$stmt->bindParam ( ":ip", $visitor_ip);
$stmt->execute ();
}
查询数据库来完成。如何解决这个问题?我使用的是Spring Boot 1.4.2.RELEASE。
$userName = $_SESSION['username'];
$query = "INSERT INTO visitorsCounter(ip, entries, user)
VALUES (:ip, 1,:user)
ON DUPLICATE KEY UPDATE entries = entries + 1;
";
$stmt = $db->prepare ( $query );
$stmt->bindParam ( ":ip", $visitor_ip);
$stmt->bindParam ( ":user", $userName);
$stmt->execute ();
if(isset($_SESSION['username'])){
$addEntryQuery = "UPDATE visitorsCounter SET user = :user WHERE ip=:ip";
$stmt = $db->prepare ( $addEntryQuery );
$stmt->bindParam ( ":ip", $visitor_ip);
$stmt->bindParam ( ":user", $userName);
$stmt->execute ();
}
原始代码如下所示:
AccountRepository:
jdbcTemplate
JdbcAccountRepository:
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| jdbcAccountRepository defined in file [repositories\JdbcAccountRepository.class]
↑ ↓
| securityConfiguration.WebSecurityConfig (field services.JdbcUserDetailsServicessecurity.SecurityConfiguration$WebSecurityConfig.userDetailsService)
↑ ↓
| jdbcUserDetailsServices (field repositories.JdbcAccountRepository services.JdbcUserDetailsServices.repository)
└─────┘
JdbcUserDetailsServices:
public interface AccountRepository {
void createAccount(Account user) throws UsernameAlreadyInUseException;
Account findAccountByUsername(String username);
}
SecurityConfiguration:
@Repository
public class JdbcAccountRepository implements AccountRepository {
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
private final JdbcTemplate jdbcTemplate;
private final PasswordEncoder passwordEncoder;
@Autowired
public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {
this.jdbcTemplate = jdbcTemplate;
this.passwordEncoder = passwordEncoder;
}
@Transactional
@Override
public void createAccount(Account user) throws UsernameAlreadyInUseException {
try {
jdbcTemplate.update(
"insert into Account (firstName, lastName, username, password, role) values (?, ?, ?, ?, ?)",
user.getFirstName(),
user.getLastName(),
user.getUsername(),
passwordEncoder.encode(
user.getPassword()),
user.getRole()
);
} catch (DuplicateKeyException e) {
throw new UsernameAlreadyInUseException(user.getUsername());
}
}
@Override
public Account findAccountByUsername(String username) {
return jdbcTemplate.queryForObject(
"select username, password, firstName, lastName, role from Account where username = ?",
(rs, rowNum) -> new Account(
rs.getString("username"),
rs.getString("password"),
rs.getString("firstName"),
rs.getString("lastName"),
rs.getString("role")),
username
);
}
}
P.S如果将Spring Boot版本降级为[1.1.5,1.2.0],则不会出现此问题(由于其他依赖性,我必须使用最新版本)
答案 0 :(得分:24)
您可以将constructor-based dependency injection替换为setter-based dependency injection来解决周期问题,请参阅Spring Framework Reference Documentation:
循环依赖
如果您主要使用构造函数注入,则可以创建无法解析的循环依赖关系场景。
例如:A类需要通过构造函数注入实现B类实例,而B类需要通过构造函数注入实现A类实例。如果将A类和B类的bean配置为相互注入,则Spring IoC容器会在运行时检测此循环引用,并抛出
BeanCurrentlyInCreationException
。一种可能的解决方案是编辑由setter而不是构造函数配置的某些类的源代码。或者,避免构造函数注入并仅使用setter注入。换句话说,虽然不推荐使用,但您可以使用setter injection配置循环依赖关系。
与典型情况(没有循环依赖关系)不同,bean A和bean B之间的循环依赖关系会强制其中一个bean在完全初始化之前注入另一个bean(一个经典的鸡/蛋场景)。 / p>
答案 1 :(得分:18)
我更喜欢@Lazy方法。这样我就可以坚持一种模式。
答案 2 :(得分:7)
您的PasswordEncoder
bean定义位于WebSecurityConfig
中,需要JdbcUserDetailsServices
。 JdbcUserDetailsServices
再次取决于需要JdbcAccountRepository
的{{1}}。这样就形成了循环。一个简单的解决方案是从PasswordEncoder
中取出PasswordEncoder
bean定义。甚至在WebSecurityConfig
类内部也可以解决循环问题。
答案 3 :(得分:1)
我今天也面临着同样的问题。我在一个类的构造函数中使用了@Lazy,它解决了我的问题:
lang-java
public class AService {
private BService b;
public ApplicantService( @NonNull @Lazy BService b){
this.b=b;
}
}
public class BService {
private AService a;
public ApplicantService( @NonNull BService a){
this.a=a;
}
}
答案 4 :(得分:1)
@Zeeshan Adnan 是对的。从 PasswordEncoder
中取出 WebSecurityConfig
解决了循环依赖问题。
答案 5 :(得分:0)
解决方案之一是不使用构造函数。例如,而不是:
private final JdbcTemplate jdbcTemplate;
private final PasswordEncoder passwordEncoder;
@Autowired
public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {
this.jdbcTemplate = jdbcTemplate;
this.passwordEncoder = passwordEncoder;
}
您可以使用:
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private PasswordEncoder passwordEncoder;